如何用CropBox实现头像裁剪并与java后台交互

如何用CropBox实现头像裁剪并与java后台交互

参考网站:https://developer.mozilla.org/zh-CN/docs/Web/API/Blob
参考:
http://blog.csdn.net/u013160024/article/details/51849732
http://www.cnblogs.com/shinefon-2-2/p/5901330.html
http://www.cnblogs.com/hhhyaaon/p/5928152.html

主流的前端jQuery 图像裁剪插件有JcropCropBox,前者是将原图和需要裁剪的参数(裁剪的各点坐标,旋转角度等)传到后台,然后由后台完成实际的裁剪和后续操作。
CropBox实现功能相对较少,但操作更简单,它的原理是:
将裁减后的图片通过base64编码,然后转化为blob格式发送到服务器,服务器完成解码即可,官网介绍可以看github上的说明和Demo
核心js函数只有两个:
getDataURL 将裁剪后的图片简单以base64编码后的结果,用于实时预览,当然也可以将它直接传到服务器,然后解码为png格式
getBlob 上传图片为Blob格式

首先贴出两个函数的源码:

 getDataURL: function ()
                {
                    var width = this.thumbBox.width(),
                        height = this.thumbBox.height(),
                        canvas = document.createElement("canvas"),
                        dim = el.css('background-position').split(' '),
                        size = el.css('background-size').split(' '),
                        dx = parseInt(dim[0]) - el.width()/2 + width/2,
                        dy = parseInt(dim[1]) - el.height()/2 + height/2,
                        dw = parseInt(size[0]),
                        dh = parseInt(size[1]),
                        sh = parseInt(this.image.height),
                        sw = parseInt(this.image.width);

                    canvas.width = width;
                    canvas.height = height;
                    var context = canvas.getContext("2d");
                    context.drawImage(this.image, 0, 0, sw, sh, dx, dy, dw, dh);
                    var imageData = canvas.toDataURL('image/png');
                    return imageData;
                },
                getBlob: function()
                {
                    var imageData = this.getDataURL();
                    var b64 = imageData.replace('data:image/png;base64,','');
                    var binary = atob(b64);
                    var array = [];
                    for (var i = 0; i < binary.length; i++) {
                        array.push(binary.charCodeAt(i));
                    }
                    return  new Blob([new Uint8Array(array)], {type: 'image/png'});
                },

1. Data URIs方式

主要利用了HTMLCanvasElement.toDataURL()方法,HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。
语法:

canvas.toDataURL(type, encoderOptions);

参数:

  • type 可选
    图片格式,默认为 image/png
  • encoderOptions 可选
    在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

返回值:
包含 data URI 的DOMString。

比如:

<canvas id="canvas" width="5" height="5"></canvas>
var canvas = document.getElementById("canvas");
var dataURL = canvas.toDataURL();
console.log(dataURL);
// "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby
// blAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC"

1.1 Data URIs

1.1.1 什么是URI

统一资源标识符(Uniform Resource Identifier,或URI)是一个用于标识某一互联网资源名称的字符串。Web上可用的每种资源 -HTML文档、图像、视频片段、程序等,都由一个统一资源标识符(Uniform Resource Identifier, 简称"URI")进行标识。

而URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。

1.1.2 怎么用

 Data URIs的数据格式很简单,通过RFC 2397的定义,一般格式是:

  data:[mime type][;charset=][;base64],

  说明:

  • data - 指代URI协议
  • mime type - 代表数据类型,如png图片则为image/png,若不说明,默认为text/plain(上面的默认值为那个函数的默认值,这里是这种数据格式的默认值)
  • charset - 如果不使用base64,则使用charset指定的字符类
  • encoded data - 对应的编码信息

1.1.3 优缺点

优点:
1.减少HTTP请求数,没有了TCP连接消耗和同一域名下浏览器的并发数限制,这里说的方式是下图这种将编码后的图片放到标签的url或者src中。

网页上的图片资源如果采用http形式的url的话都会额外发送一次请求,网页发送的http请求次数越多,会造成页面加载速度越慢。而采用Base64格式的编码,将图片转化为字符串后,图片文件会随着html元素一并加载,这样就可以减少http请求的次数,对于网页优化是一种比较好的手段。

2.对于小文件会降低带宽。虽然编码后数据量会增加,但是却减少了http头,当http头的数据量大于文件编码的增量,那么就会降低带宽。
3.采用Base64编码的图片是随着页面一起加载的,不会造成跨域请求的问题,也不会造成清理图片缓存的问题。

缺点:
1.无法被重复利用,同一个文档应用多次同一个内容,则需要重复多次,数据量大量增加,增加了下载时间。
2.不支持数据压缩,base64编码大小会增加1/3左右,而urlencode后数据量会增加更多。
3.当我们将一个只有几KB的图片转化为Base64格式编码,生成的字符串往往会大于几KB,如果将其写在一个css文件中,这样一个css文件的大小会剧增,造成代码可读性差不说,还会造成请求传输的数据量递增。
4.如果我们将Base64编码的图片存入数据库中,会造成数据库数据量的增大,这样的效果还不如将图片存至图片服务器,而只在数据库中存入url字段。
5.不利于安全软件的过滤,同时也存在一定的安全隐患。

由于我们会将图片传输到服务器,为减小传输数据量,采用了Blob。

2. Blob(二进制大对象)方式

HTMLCanvasElement.toBlob() 方法创造Blob对象,用以展示canvas上的图片;这个图片文件可以被缓存或保存到本地,由用户代理端自行决定。如不特别指明,图片的类型默认为 image/png,分辨率为96dpi。
语法:

void canvas.toBlob(callback, type, encoderOptions);

参数:

  • callback
    回调函数,可获得一个单独的Blob对象参数。
  • type 可选
    DOMString类型,指定图片格式,默认格式为image/png。
  • encoderOptions 可选
    Number类型,值在0与1之间,当请求图片格式为image/jpeg或者image/webp时用来指定图片展示质量。

比如将canvas图像转换为文件,当一个内容画到canvas上时,我们可以将它生成任何一个格式支持的图片文件。比如,下面的代码段获得了id为“canvas”的<canvas>元素中的图像,复制成一个PNG图,在文档中加入一个新的<img>元素,这个<img>元素的源图就是使用canvas创建的那个图像:

var canvas = document.getElementById("canvas");

canvas.toBlob(function(blob) {
  var newImg = document.createElement("img"),
      url = URL.createObjectURL(blob);

  newImg.onload = function() {
    // no longer need to read the blob so it's revoked(撤销,删除)
    URL.revokeObjectURL(url);
  };

  newImg.src = url;
  document.body.appendChild(newImg);
});

注意,我们这里创建的是PNG图片;如果在toBlob()传入第二个参数,就可以指定图片格式。例如,生成JPEG格式的图片:

canvas.toBlob(function(blob){...}, "image/jpeg", 0.95); // JPEG at 95% quality

2.1 new Blob([new Uint8Array(array)], {type: 'image/png'})

当然CropBox并没有使用toBlob方法,而是直接利用js中的Blob对象类型的构造方法。
语法:

var aBlob = new Blob( array, options );

参数:

  • array 是一个由ArrayBuffer, ArrayBufferView, Blob, DOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob.
  • options 是一个可选的Blob熟悉字典,它可能会指定如下两种属性:
    type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型。
    endings,默认值为"transparent",它代表包含行结束符 的字符串如何被输出。 它是以下两个值中的一个: "native",代表行结束符会被更改为适合宿主操作系统文件系统的惯例,或者 "transparent", 代表会保持blob中保存的结束符不变
    比如:
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; // an array consisting of a single DOMString
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // the blob

用处:

  1. 大文件分割 (slice() 方法):
    slice() 方法接受三个参数,起始偏移量,结束偏移量,还有可选的 mime 类型,然后轮循向后台提交各文件片段,即可实现文件的分片上传。

3 Base64编码

Base64编码本质上是一种将二进制数据转成文本数据的方案。对于非二进制数据,是先将其转换成二进制形式,然后每连续6比特(2的6次方=64)计算其十进制值,根据该值在大小为64的码表中找到对应的字符,最终得到一个文本字符串。

Base64编码的作用:

  • 由于某些系统中只能使用ASCII字符。Base64用来将非ASCII字符的数据转换成ASCII字符。
    比如我们的电子邮件系统,一般是使用SMTP(简单邮件传输协议)将邮件从客户端发往服务器端,邮件客户端使用POP3(邮局协议,第3版本)或IMAP(交互邮件访问协议)从服务器端获取邮件。

SMTP协议一开始是基于纯ASCII文本的,对于二进制文件(比如邮件附件中的图像、声音等)的处理并不好,因为标准ASCII编码最高位不是数据位,会把二进制文件的最高位作为不可见字符,可能传输的过程中当做校验位处理掉了,从而导致传输错误。Base64可以将非ASCII字符的数据转换成ASCII字符。

标准ASCII码的最高位是奇偶校验位,比如奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添

  • HTML内嵌Base64编码图片
    前端在实现页面时,对于一些简单图片,通常会选择将图片内容直接内嵌在页面中,避免不必要的外部资源加载和Http请求,比如Data URIs,允许使用Base64对图片或其他文件的二进制数据进行编码,将其作为文本字符串嵌入网页中。以百度搜索首页为例,其中语音搜索的图标是个背景图片,其内容以 Data URLs 形式直接写在css中,这个css内容又直接嵌在HTML页面中,如下图所示:

  • 很多场景下的数据传输要求数据只能由简单通用的字符组成,比如HTTP协议要求请求的首行和请求头都必须是ASCII编码。Base-64编码将用户输入或二进制数据,打包成一种安全格式,将其作为HTTP首部字段的值发送出去,而无须担心其中包含会破坏HTTP分析程序的冒号、换行符或二进制值。

原理

base64其实不是安全领域下的加密解密算法。虽然有时候经常看到所谓的base64加密解密。其实base64只能算是一个编码算法,对数据内容进行编码来适合传输。虽然base64编码过后原文也变成不能看到的字符格式,但也仅此而已。
它的算法是:每3个字节(每字节8bit),转换为4个6bit的字节(一个字节应该是8bit,所以前2位补0)。例如:

xxxxxxxx yyyyyyyy xxxxyyyy这里转换前的3个字节,然后,每6位分到一个字节中:

xxxxxx xxyyyy yyyyxx xxyyyy

然后高位补0

00xxxxxx 00xxyyyy 00yyyyxx 00xxyyyy

其中xy是二进制的0和1,然后再按base64码表进行替换(base64,基本的64个码,=号不在其内),base64编码后的字符串只包含字母A-Z,a-z,数字0-9,还有+/这2个特殊字符。

也就是说,转换后的字符串理论上将要比原来的长1/3。因此Base64所造成数据冗余不是很严重,Base64是当今比较流行的编码方法,因为它编起来速度快而且简单

举个例子,有三个字节的原始数据:aaaaaabb bbbbccccc ccdddddd(这里每个字母表示一个bit位)
  那么编码之后会变成:      00aaaaaa 00bbbbbb 00cccccc 00dddddd

所以可以看出base64编码简单,虽然编码后不是明文,看不出原文,但是解码也很简单
原文的字节不够的地方可以用全0来补足,转换时Base64编码用=号来代替。

4. 实现

上面的知识点纯属复制粘贴,复习一下知识点,下面开始实现代码(源码)。
项目的目录结构:

采用Spring+SpringMVC+MyBatis+MySQL+Maven+CropBox实现,主要功能:

  • 图片上传,裁剪
  • 预览
  • 后台保存到数据库和文件夹
  • 缩放输出

    下面为从后台数据库或者文件夹取出的图片,然后byte[]格式传到前台, 前一个头像正常大小,后一个缩放到100*100像素

    下面贴出主要代码:

视图层

cropbox用法:
github上的说明和Demo

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CropBox头像裁剪,上传,回显</title>
<link rel="stylesheet" href="/css/style.css" type="text/css" />
</head>
<body>
<script type="text/javascript" src="/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/js/cropbox.js"></script>
<div class="container">
  <div class="imageBox">
    <div class="thumbBox"></div>
    <div class="spinner" style="display: none">Loading...</div>
  </div>
    <div class="action">
      <!-- <input type="file" id="file" style="  200px">-->
      <div class="new-contentarea tc"><a href="javascript:void(0)" class="upload-img">
        <label for="upload-file">上传图像</label>
      </a>
        <input type="file" class="" name="upload-file" id="upload-file"/>
      </div>
      <input type="button" id="btnCrop" class="Btnsty_peyton" value="裁切">
      <input type="button" id="btnZoomIn" class="Btnsty_peyton" value="+">
      <input type="button" id="btnZoomOut" class="Btnsty_peyton" value="-">
      <input type="button" id="blobSubmit" class="Btnsty_peyton" value="提交">
    </div>
    <div class="cropped"></div>
</div>
<script type="text/javascript">
$(window).load(function() {
	var options =
	{
		thumbBox: '.thumbBox',
		spinner: '.spinner',
		imgSrc: 'images/avatar.png'
	}
	var cropper = $('.imageBox').cropbox(options);
	$('#upload-file').on('change', function(){
		var reader = new FileReader();
		reader.onload = function(e) {
			options.imgSrc = e.target.result;
			cropper = $('.imageBox').cropbox(options);
		}
		reader.readAsDataURL(this.files[0]);
		this.files = [];
	})
    $('#blobSubmit').on('click', function(){
        var img = cropper.getBlob();
        var formdata = new FormData();
        formdata.append("imagefile", img);
        $.ajax({
            url:"/file/updateHeadPicture.action",
            data: formdata,
            type:"post",
            //默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),
            // 都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
            processData: false,
            contentType: false,
            success: function(oResult) {
                if(oResult.success==1){
                    window.location.href="/image";
                }else{
                    alert(oResult.message);
                }
            }
        })
    })
	$('#btnCrop').on('click', function(){
		var img = cropper.getDataURL();
		$('.cropped').html('');
		$('.cropped').append('<img src="'+img+'" align="absmiddle" style="64px;margin-top:4px;border-radius:64px;box-shadow:0px 0px 12px #7E7E7E;" ><p>64px*64px</p>');
		$('.cropped').append('<img src="'+img+'" align="absmiddle" style="128px;margin-top:4px;border-radius:128px;box-shadow:0px 0px 12px #7E7E7E;"><p>128px*128px</p>');
		$('.cropped').append('<img src="'+img+'" align="absmiddle" style="180px;margin-top:4px;border-radius:180px;box-shadow:0px 0px 12px #7E7E7E;"><p>180px*180px</p>');
	})
	$('#btnZoomIn').on('click', function(){
		cropper.zoomIn();
	})
	$('#btnZoomOut').on('click', function(){
		cropper.zoomOut();
	})
});
</script>
</div>
</body>
</html>
  • image.html
    从后台获得byte[]格式的字节流,然后展示经过后台处理后的效果:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>头像展示</title>
</head>
<body>
<div id="forAppend" class="demo"></div>
<img src="/image/xie" alt=""/>
##单位是像素,并且是按照长和宽中较小的值来确定等比例缩放的比例
<img src="/image/xie/300/100" alt=""/>
</body>

</html>

控制层

package com.cropbox.demo.uploadHead.controller;

import com.alibaba.fastjson.JSON;
import com.cropbox.demo.uploadHead.mapper.UserMapper;
import com.cropbox.demo.uploadHead.model.UploadPictureResponse;
import com.cropbox.demo.uploadHead.model.User;
import com.cropbox.demo.uploadHead.service.UploadService;
import com.cropbox.demo.uploadHead.utils.ImageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * getBlob:上传图片为Blob格式,并保存到mysql的blob字段中,
 * 其实File继承了Blob,所以表单中的图片处理方式与之类似,
 * 实现头像的裁剪,保存到服务器,并在需要时回显到客户端
 *
 * @author xie
 * @version 1.0
 * @Date 2017/5/26
 */
@Controller
public class FileController {
    @Autowired
    UploadService uploadService;

    @Autowired
    UserMapper userMapper;

    @Autowired
    ImageUtils imageUtils;

    /**
     * 主页
     * @return
     */
    @RequestMapping(path = {"/"}, method = {RequestMethod.GET, RequestMethod.POST})
    public String index() {
        return "index";
    }

    /**
     * 实现图片上传
     * @param file
     * @param response
     */
    @RequestMapping(path = {"/file/updateHeadPicture.action"}, method = {RequestMethod.GET, RequestMethod.POST})
    public void index(@RequestParam("imagefile") MultipartFile file, HttpServletResponse response) {
        try {
            UploadPictureResponse uploadPictureResponse = uploadService.updateHeadPicture(file);
                 /*
                 设置编码格式,返回结果json结果,注意其中的对象转化为json字符串格式为:
                 {"message":"上传图片成功!","success":1,"url":"C:\\home\\myblog\\pic\\2f1b63bc4b654a27a7e0c1b1a0fb9270.png"}
                 所以前端可以直接读取success,message等信息
                 */
            response.setContentType( "application/json;charset=UTF-8");
            response.getWriter().write( JSON.toJSONString(uploadPictureResponse));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    @RequestMapping(path= {"/image"}, method = {RequestMethod.GET, RequestMethod.POST})
    public String index1() {
        return "image";
    }

    /**
     * 按照用户名查找头像
     * @param username
     * @param response
     */
    @RequestMapping(path = {"/image/{username}"}, method = {RequestMethod.GET, RequestMethod.POST})
    public void index1(@PathVariable("username") String username, HttpServletResponse response) {
        User user = userMapper.selectByUsername(username);
        try {
            //写到输出流
            response.setContentType("image/png");
            response.setCharacterEncoding("UTF-8");
            //BufferedOutputStream 是缓冲输出流,默认新建字节数组大小为8192的“缓冲输出流”
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            outputStream.write(user.getHead());
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 按照用户名查找头像,并提供缩放功能
     * @param username 用户名
     * @param width 要求图片的宽度
     * @param height 要求图片的高度
     * @param response
     */
    @RequestMapping(path = "/image/{username}/{width}/{height}")
    public void getPhotoById(@PathVariable("username") String username, @PathVariable("width") int width,
                             @PathVariable("height") int height, HttpServletResponse response) {
        User user = userMapper.selectByUsername(username);
        byte[] data = user.getHead();
        try {
            if (width > 0 && height > 0) {
                data = imageUtils.scaleImage(data, width, height);
            }
            response.setContentType("image/png");
            response.setCharacterEncoding("UTF-8");
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            outputStream.write(data);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

service层

package com.cropbox.demo.uploadHead.service;

import com.cropbox.demo.uploadHead.mapper.UserMapper;
import com.cropbox.demo.uploadHead.model.UploadPictureResponse;
import com.cropbox.demo.uploadHead.utils.ImageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.UUID;

/**
 * 类的详细说明
 *
 * @author xie
 * @version 1.0
 * @Date 2017/5/27
 */
@Service
public class UploadService {

    @Autowired
    ImageUtils imageUtils;

    @Autowired
    UserMapper userMapper;

    /**
     * 上传的头像统一都转换为了png格式,故不进行是否允许类型判断
     * @param file
     * @return
     * @throws IOException
     */
    public UploadPictureResponse updateHeadPicture(MultipartFile file) throws IOException {
      UploadPictureResponse uploadPictureResponse = new UploadPictureResponse();
        try {
            InputStream is = file.getInputStream();
            byte[] bytes = FileCopyUtils.copyToByteArray(is);
            //更新数据库中的blob格式的head字段,返回1表示更新成功,返回0表示失败
            int success = userMapper.updateHead(1,bytes);
            //上面已经将输入流中的数据全部读完,故重新初始化
            is = file.getInputStream();
            //同时将图片保存到C:\home\myblog\pic\ 路径下,这里保存到文件夹只是演示作用,请根据需求决定将图片保存到数据库还是服务器文件夹
            String fileName = UUID.randomUUID().toString().replaceAll("-", "") + ".png" ;
            Files.copy(is, new File(imageUtils.getPictureDir() + fileName).toPath(),
                    StandardCopyOption.REPLACE_EXISTING);
            uploadPictureResponse.setSuccess(success);
            uploadPictureResponse.setMessage("上传图片成功!");
            uploadPictureResponse.setUrl(imageUtils.getPictureDir() + fileName);
            is.close();
            return uploadPictureResponse;
        } catch (Exception e) {
            // 请求失败时打印的异常的信息
            uploadPictureResponse.setSuccess(0);
            uploadPictureResponse.setMessage("服务器异常!");
            return uploadPictureResponse;
        }
    }

}

图片处理工具类

package com.cropbox.demo.uploadHead.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 图片处理服务
 *
 * @author xie
 * @version 1.0
 * @Date 2017/5/27
 */
@Service
public class ImageUtils {

    /** 头像图片的放置路径*/
    @Value("${headPath.home}")
    private String PictureDir;

    /** 允许的图片类型头像图片,这里分别使用属性占位符和SpEL表达式,可以实现更复杂的功能,运行时计算值*/
    @Value("${pictureLimit.suffix}")
    private String PictureFileSuffix;

    /**
    判断上传图片格式是否被允许
     */
    public boolean isFileAllowed(String fileSuffix) {
        for (String suffix : PictureFileSuffix.split(",")) {
            if (suffix.equals(fileSuffix)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获得图片存储路径
     * @return
     */
    public String getPictureDir(){
        return PictureDir;
    }

    /**
     * 获得系统允许上传图片后缀
     * @return
     */
    public String getPictureFileSuffix(){
        return PictureFileSuffix;
    }

    /**
     * 等比例缩放图片,按照长和宽中较小的数来确定缩放比例,所有单位为像素,
     * 在传输中,图片是不能直接传的,因此需要把图片变为字节数组,然后传输比较方便;只需要一般输出流的write方法即可;而字节数组变成BufferedImage能够还原图像;
     *
     * @param data 图片的byte[]格式
     * @param width 缩放后的宽度
     * @param height 缩放后的高度
     * @return 图片缩放后的byte[]格式
     * @throws IOException
     */
    public byte[] scaleImage(byte[] data, int width, int height) throws IOException {
        ////从特定文件载入
        BufferedImage oldImage = ImageIO.read(new ByteArrayInputStream(data));
        int imageOldWidth = oldImage.getWidth();
        int imageOldHeight = oldImage.getHeight();
        double scale_x = (double) width / imageOldWidth;
        double scale_y = (double) height / imageOldHeight;
        double scale_xy = Math.min(scale_x, scale_y);
        int imageNewWidth = (int) (imageOldWidth * scale_xy);
        int imageNewHeight = (int) (imageOldHeight * scale_xy);

        //创建一个不带透明色的BufferedImage对象
        BufferedImage newImage = new BufferedImage(imageNewWidth, imageNewHeight, BufferedImage.TYPE_INT_RGB);

        /*BufferedImage与Image之间的相互转换,其中
        * oldImage.getScaledInstance(imageNewWidth, imageNewHeight, BufferedImage.SCALE_SMOOTH)表示缩放图像
        * BufferedImage.SCALE_SMOOTH表示压缩图片所用的算法,本算法生成缩略图片的平滑度的优先级比速度高,生成的图片质量比较好,但速度慢
        *
         */
        newImage.getGraphics().drawImage(oldImage.getScaledInstance(imageNewWidth, imageNewHeight, BufferedImage.SCALE_SMOOTH), 0, 0, null);

        /*
        释放绘图上下文所占的系统资源
         */
        newImage.getGraphics().dispose();
        ByteArrayOutputStream outPutStream = new ByteArrayOutputStream();

        /*BufferedImage  ---->byte[],
        参数newImage表示获得的BufferedImage;
        参数format表示图片的格式,比如“gif”等;
        参数out表示输出流,如果要转成Byte数组,则输出流为ByteArrayOutputStream即可;
        执行完后,只需要toByteArray()就能得到byte[];
        */
        ImageIO.write(newImage, "jpg", outPutStream);
        oldImage.flush();

        outPutStream.flush();
        outPutStream.close();
        return outPutStream.toByteArray();
    }
}

分析

利用Fiddler我们简要分析一下http的请求与回应。
如下图所示,当我们点击了提交按钮

http各字段的含义:http://www.cnblogs.com/xzwblog/p/6917960.html

其中:
Content-Length: 78174表示传输的二进制图片的大小,单位字节
Content-Disposition: form-data; name="imagefile"; filename="blob"作为对下载文件的一个标识字段。
在请求时,form-data表示上传表单数据,name="imagefile"表示表单参数的名字, filename="blob"表示文件名
在回应时,Content-Disposition有两种属性,inline 和 attachment。 inline :将文件内容直接显示在页面, attachment:弹出对话框让用户下载。
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrmcDIMhUIGDLILq1表示利用表单提交数据,boundary是为了区分POST的内容的区段用的,只要在内容中遇到了此值,就表示下面要开始一个新的区段了,每个区段的内容相对独立。如果遇到的是此值后面连着两个减号,则表示全部内容到此结束。每个段也分为段头和段体两部分,用空行隔开,每段都有自己的类型和相关信息。比如下面的请求,第一区段是text1的值,它的名称是“text1”,值为“hehe”。第二段是文件内容,段首里表明了此文件域的名称“file1”和此文件在用户磁盘上的位置,后面就是文件的内容。

POST /form.asp HTTP/1.1
Accept: */*
Referer: http://localhost:8080/form.asp
Accept-Language: zh-cn
Content-Type: multipart/form-data; boundary=---------------------------7d62bf2f9066c
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; InfoPath.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Content-Length: 337
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: key=haha; ASPSESSIONIDCSQCRTBS=LOIPGIMBLMNOGCOBOMPJBOKP
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="text1"

hehe
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="file1"; filename="H:Documents and SettingsAdministrator桌面haha.txt"
Content-Type: text/plain

ABCDEFG
-----------------------------7d62bf2f9066c--

{"message":"上传图片成功!","success":1,"url":"C:\\home\\myblog\\pic\\3973974dd2bb41b49fe064dedc0dcae9.png"}表示回应的json字符串,即项目中的JSON.toJSONString(uploadPictureResponse)部分,将uploadPictureResponse实体对象转化为json字符串。

源码

原文地址:https://www.cnblogs.com/xzwblog/p/6912320.html