首先,这款插件是基于前端框架Bootstrap而制作的,要引入Bootstrap所必需的js 以及css, 还有Jquery
<script src="${APP_PATH}/static/js/jquery-1.11.1.min.js"></script> <link href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet"> <script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> <link href="${APP_PATH}/static/imageCut/cropper.min.css" rel="stylesheet"> <link href="${APP_PATH}/static/imageCut/sitelogo.css" rel="stylesheet"> <script src="${APP_PATH}/static/imageCut/cropper.min.js"></script> <script src="${APP_PATH}/static/imageCut/sitelogo.js"></script>
先简单介绍一下工作原理
1.先选取一张图片
2.前端通过js获取剪裁开始点的坐标,以及选取的图片大小
3.通过Ajax异步上传原始图片,以及剪裁的数据
4.后台通过java图片操作剪取图片
5.返回图片保存的路径,在前端显示
国际惯例,先上几张效果图:
移动选取框获取剪裁数据
点击保存后,后台处理,得到返回的图片地址,显示在前台
nice!
接下来,介绍一下代码:
为了后台方便处理剪裁数据,采用Math.floor()取整
startCropper: function () { var _this = this; if (this.active) { this.$img.cropper('replace', this.url); } else { this.$img = $('<img src="' + this.url + '">'); this.$avatarWrapper.empty().html(this.$img); this.$img.cropper({ aspectRatio: 1, preview: this.$avatarPreview.selector, strict: false, crop: function (data) { var json = [ '{"x":' + Math.floor(data.x), '"y":' + Math.floor(data.y), '"height":' + Math.floor(data.height), '"width":' + Math.floor(data.width), '"rotate":' + data.rotate + '}' ].join(); _this.$avatarData.val(json); } }); this.active = true; } },
把文件和数据封装为FormData对象 一起通过ajax请求传给后台(注意里面的这两句设为false)
processData: false,
contentType: false,
ajaxUpload: function () { var url = this.$avatarForm.attr('action'), data = new FormData(this.$avatarForm[0]), _this = this; $.ajax(url, { headers: {'X-XSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}, type: 'post', data: data, /*dataType: 'json',*/ processData: false, contentType: false, beforeSend: function () { _this.submitStart(); }, success: function (data) { _this.submitDone(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { _this.submitFail(textStatus || errorThrown); }, complete: function () { _this.submitEnd(); } }); },
当然,前端有验证的
isImageFile: function (file) { if (file.type) { return /^image\/\w+$/.test(file.type); } else { return /\.(jpg|jpeg|png|gif)$/.test(file); } },
否则不给上传,并提示信息
submit: function () { if (!this.$avatarSrc.val() && !this.$avatarInput.val()) { return false; } var files = this.$avatarInput.prop('files'); if(!this.isImageFile(files[0])){ return false; } if (this.support.formData) { this.ajaxUpload(); return false; } },
这是ajax请求数据
这里是后台的Controller层处理ajax请求,对图片进行裁剪操作,并且返回一个map对象
里面存有 键为 result ,值为裁剪后的图片地址 的一个键值对
@Controller public class SomeController { @Autowired private UserService userService; @RequestMapping("changeUserPic") @ResponseBody public Map<String, String> changeUserPic(HttpServletRequest request, @RequestParam("avatar_file") MultipartFile file, @RequestParam("avatar_data") String dataStr,HttpSession session) throws IllegalStateException, IOException{ JSONObject data = JSONObject.fromObject(dataStr); int height = (int) data.get("height"); int width = (int) data.get("width"); int startX = (int) data.get("x"); int startY = (int)data.get("y"); Map<String, String> map = new HashMap<String, String>(); if(!file.isEmpty()&&(height > 0 && width > 0 && startX > 0 && startY > 0)) { //上传文件路径 String path = request.getServletContext().getRealPath("/userImageFile/"); //上传文件名 String filename = file.getOriginalFilename(); String fileType = filename.substring(filename.indexOf(".") + 1).toLowerCase(); String name = UUID.randomUUID().toString()+ filename.substring(filename.indexOf(".")); File filepath = new File(path,name); //判断路径是否存在,如果不存在就创建一个 if (!filepath.getParentFile().exists()) { filepath.getParentFile().mkdirs(); } //将上传文件保存到一个目标文件当中 String imagepath = path + File.separator + name; System.out.println(imagepath); file.transferTo(new File(imagepath)); //开始剪裁 ImageUtils test = new ImageUtils(); test.input = new FileInputStream(imagepath); test.out = imagepath; test.cutImage(fileType,startX, startY, width, height); //更新数据库 String picPath = request.getContextPath()+File.separator+"userImageFile"+File.separator+ name; userService.updateUser(session.getAttribute("username").toString(), picPath); session.setAttribute("userPic", picPath); map.put("result", picPath); } //前台做了校验,不能上传空文件 return map; } }
ajax请求成功后,接收返回的result (我没有返回message), 并且更新前台数据
submitDone: function (data) { if ($.isPlainObject(data)) { if (data.result) { this.url = data.result; if (this.support.datauri || this.uploaded) { this.uploaded = false; this.cropDone(); } else { this.uploaded = true; this.$avatarSrc.val(this.url); this.startCropper(); } this.$avatarInput.val(''); } else if (data.message) { this.alert(data.message); } } else { this.alert('Failed to response'); } },
这个是对图片的裁剪操作代码
public class ImageUtils { public FileInputStream input = null; public FileOutputStream output = null; //输出图片地址 public String out = null; public void cutImage(String type,int x, int y, int width, int height) throws IOException { ImageInputStream imageStream = null; try { String imageType=(null==type||"".equals(type))?"jpg":type; Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(imageType); ImageReader reader = readers.next(); imageStream = ImageIO.createImageInputStream(input); reader.setInput(imageStream, true); ImageReadParam param = reader.getDefaultReadParam(); Rectangle rect = new Rectangle(x, y, width, height); param.setSourceRegion(rect); BufferedImage bi = reader.read(0, param); ImageIO.write(bi, imageType, new File(out)); /*ImageIO.write(bi, imageType, output); */ /* * 假如输出到原图片地址,ImageIO.write(),,第三个参数必须 new File * 而不是一个 FileOutputStream对象 ,否则报错 */ } finally { if (input != null ) input.close() ; if (imageStream != null ) imageStream.close(); } } }
对了,关于图像旋转后剪裁是会出问题的,因为后台处理的是未旋转的原图片,想要实现旋转的效果,可以根据ajax传递的参数
rotate的值(90,180,270,),来相应的更改起点坐标,小伙伴们可以自己实现
对json数据的操作,我引用的是这个jar包,pom依赖是:
<!-- json所需jar包 注明jdk版本,否则无法下载--> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency>
哈哈哈!大功告成
想研究一下代码的小伙伴可以到下面这个地址下载