您的位置:首页 > 编程语言 > Java开发

用CropBox实现用户头像裁剪上传与Java后台交互

2017-11-21 15:53 417 查看
参考网站: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 图像裁剪插件有Jcrop和CropBox,前者是将原图和需要裁剪的参数(裁剪的各点坐标,旋转角度等)传到后台,然后由后台完成实际的裁剪和后续操作。

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'});
},


下面贴出主要代码:

视图层

index.html

实现头像裁剪,上传,预览,

这里利用了jQuery.ajax()函数,不懂得去复习下吧

官网

我的博客里也有简要介绍:http://www.cnblogs.com/xzwblog/p/6915213.html#_label5

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">

4000
<!-- <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的请求与回应。

如下图所示,当我们点击了提交按钮

。。。。。详情请参考:

https://www.cnblogs.com/xzwblog/p/6912320.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息