如何用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",它代表包含行结束符\n的字符串如何被输出。 它是以下两个值中的一个: "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=" width: 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="width: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="width: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="width: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 Settings\Administrator\桌面\haha.txt"
Content-Type: text/plain ABCDEFG
-----------------------------7d62bf2f9066c--

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

源码

如何用CropBox实现头像裁剪并与java后台交互的更多相关文章

  1. cropbox.js 头像裁剪插件

    cropbox.js 一个轻量级和简单的JavaScript,Jquery,YUI插件来裁剪您的头像. 特征 支持dataUrl显示图像(函数getDataURL) 支持Blob上传图片(函数getB ...

  2. 基于jQuery头像裁剪插件cropbox

    今天给大家分享一款基于jQuery头像裁剪插件cropbox,这是一款简单实用的jQuery头像在线裁剪插件.该插件适用于适用浏览器:IE8.360.FireFox.Chrome.Safari.Ope ...

  3. 用JQuery仿造QQ头像裁剪功能

    最近工作真心忙碌,几乎没时间写博客.今天趁周末来仿一个QQ头像裁剪功能插件.效果如下: 所有文件都可在我的Github上下载,从头到尾从简到繁按步骤一共分了9个HTML文件,每个步骤文件里的注释都写的 ...

  4. 第三百九十节,Django+Xadmin打造上线标准的在线教育平台—Django+cropper插件头像裁剪上传

    第三百九十节,Django+Xadmin打造上线标准的在线教育平台—Django+cropper插件头像裁剪上传 实现原理 前台用cropper插件,将用户上传头像时裁剪图片的坐标和图片,传到逻辑处理 ...

  5. springMVC 头像裁剪上传并等比压

    第一次写头像裁剪上传,原本想着直接本地预览裁剪再上传,可是时间有限,jquery.jcrop貌似并没有对 假设是ie下图片预览效果是滤镜做的  做出对应处理,也没有时间去改;仅仅好将就一下先把图片上传 ...

  6. AndroidClipSquare安卓实现方形头像裁剪

    安卓实现方形头像裁剪 实现思路.界面可见区域为2层View 最顶层的View是显示层,主要绘制半透明边框区域和白色裁剪区域,代码比較easy. 第二层继承ImageView,使用ImageView的M ...

  7. jQuery实现用户头像裁剪插件cropbox.js

    几乎每一个网页是必备图片上传,图片裁剪功能,这里通过cropbox.js插件实现该功能. <script src="js/jquery-1.11.1.min.js">& ...

  8. cropbox插件实现的头像裁剪效果

    html代码 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UT ...

  9. 【绝对干货】仿微信QQ设置图形头像裁剪,让你的App从此炫起来~

    最近在做毕业设计,想有一个功能和QQ一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流. 而纵观现在主流的APP,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue ...

随机推荐

  1. 201521123018 《Java程序设计》第10周学习总结

    1. 本章学习总结 你对于本章知识的学习总结 2. 书面作业 一.inally 题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? 答: 4-2中 ...

  2. Java课程设计 购物车系统(个人博客)

    1. 团队课程设计博客链接 课程设计 2. 个人负责模块或任务说明 编写ShoppingCart类,连接数据库 编写updateCart类,从数据库中获取商品信息,获取指定编号的商品信息 编写User ...

  3. delphi cxrid设置column靠左显示

    1.双击cxgrid控件,选中要设置的column 2.找到properties,将column设置为Textedit,点击左边的加号 3.点击ALignment->Horz选中taleftJu ...

  4. JavaScript遍历对象-总结一

    原生JavaScript 遍历 1.for 循环遍历 let array1 = ['a','b','c']; for (let i = 0;i < array1.length;i++){ con ...

  5. Android Framework 初探

    最近工作任务不忙,学习一下Android Framework方面的知识. 一.介绍,是什么 Android的Framework是直接应用之下的一层,叫做应用程序框架层.这一层是核心应用程序所使用的AP ...

  6. 【转】独立游戏如何对接STEAM SDK

    独立开发者在对接STEAM SDK之前 首先得先登上青睐之光,也就是我们俗称的"绿光" 一般要先对接G胖家的SDK,然后提交版本,最后等待审核... 我本身是unity 开发,对C ...

  7. 【JVM命令系列】jstack

    jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使 ...

  8. HDFS概述(1)————HDFS架构

    概述 Hadoop分布式文件系统(HDFS)是一种分布式文件系统,用于在普通商用硬件上运行.它与现有的分布式文件系统有许多相似之处.然而,与其他分布式文件系统的区别很大.HDFS具有高度的容错能力,旨 ...

  9. Cow Exhibition 变种背包

    Cow Exhibition Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Subm ...

  10. Intellij idea史上最简单的教程之Linux下安装与破解Intellij idea2017

    一.前言 这一节我们介绍在Linux下如何安装与破解Intellij idea2017.现在有很多公司开发环境都是Linux,所以掌握在Linux环境下使用Idea办公也是咱们必须得掌握的技能. 记住 ...