1、整体效果图:
2、实现原理:
(1)利用input的onchange事件异步上传图片到服务器
(2)获取上传图片的相对地址,回显到img标签中
(3)利用jcrop裁剪图片,提交给后台裁剪的起始坐标,宽度、高度
(4)后台裁剪图片并保存
3、用到的插件:
(1)JCrop图片裁剪插件
(2)异步上传图片插件:Ajaxfileupload.js
4、实现:
(1)jsp关键代码:
<fieldset> <legend>头像上传</legend> <!-- 显示图片的img --> <img src="" id="realPic" width="400px" height="400px" /> <!-- 缩略图预览 --> <div id="preview-pane"> <div class="preview-container"> <img src="" width="150px" height="150px" /> </div> </div> <div class="container"> <!-- 打开图片控制 --> <span class="btn btn-success fileinput-button"> <i class="icon-plus icon-white"></i> <span>选择图片</span> <input type="file" onchange="ajaxFileUpload()" name="realPicFile" id="realPicFile" multiple /> </span> <!-- 这种做法IE不支持,拒绝访问. <input id="realPicFile" value="选择图片" onchange="ajaxFileUpload()" type="file" style="display: none;" name="realPicFile" /> <a class="btn btn-success" href="javascript:selectPic();" > <i class="icon-plus icon-white"></i> 选择图片 </a> --> <!-- 上传并裁剪图片 --> <img src="${ctx}/images/ajax-loader.gif" id="loading" style="display: none;"> <a class="btn btn-success" href="javascript:cutPic();"><i class="icon-picture icon-white"></i>保存头像</a> <!-- 获取裁剪的起始坐标和宽度、高度给后台 --> <form id="coords" class="coords"> <div class="inline-labels"> <input type="hidden" size="4" id="x1" name="x1" /> <input type="hidden" size="4" id="y1" name="y1" /> <input type="hidden" size="4" id="x2" name="x2" /> <input type="hidden" size="4" id="y2" name="y2" /> <input type="hidden" size="4" id="w" name="w" /> <input type="hidden" size="4" id="h" name="h" /> </div> </form> </div> </fieldset>
控制样式的CSS :
.jcrop-holder #preview-pane { width:156px; height:156px; display: block; position: absolute; /*z-index: 2000;*/ top: 0px; right: -200px; padding: 6px; border: 1px rgba(0,0,0,.4) solid; background-color: white; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2); -moz-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2); box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2); } /* The Javascript code will set the aspect ratio of the crop area based on the size of the thumbnail preview, specified here */ #preview-pane .preview-container { width: 156px; height: 156px; overflow: hidden; } #target-pane { width: 400px; height: 400px; } .fileinput-button { position: relative; overflow: hidden; } .fileinput-button input { position: absolute; top: 0; right: 0; margin: 0; opacity: 0; filter: alpha(opacity=0); transform: translate(-300px, 0) scale(4); font-size: 23px; direction: ltr; cursor: pointer; }
//Create variables (in this scope) to hold the API and image size var jcrop_api, boundx, boundy, path; /** * 更新缩略图,实现原理:根据原图框选的内容,显示到缩略图上,而缩略图也是原图是进行了放大,只是超过img范围的部分被隐藏 */ function updatePreview(c) { if (parseInt(c.w) > 0) { $('#x1').val(c.x); $('#y1').val(c.y); $('#x2').val(c.x2); $('#y2').val(c.y2); $('#w').val(c.w); $('#h').val(c.h); var rx = xsize / c.w; var ry = ysize / c.h; // 精确计算图片的位置 $pimg.css({ width : Math.round(rx * boundx) + 'px', height : Math.round(ry * boundy) + 'px', marginLeft : '-' + Math.round(rx * c.x) + 'px', marginTop : '-' + Math.round(ry * c.y) + 'px' }); } } /** * 异步上传图片 * @returns {Boolean} */ function ajaxFileUpload() { $("#loading").ajaxStart(function() { $(this).show(); })//开始上传文件时显示一个图片 .ajaxComplete(function() { $(this).hide(); });//文件上传完成将图片隐藏起来 var file = $("#realPicFile").val(); if(!/\.(gif|jpg|jpeg|png|JPG|PNG)$/.test(file)) { Error("不支持的图片格式.图片类型必须是.jpeg,jpg,png,gif格式."); return false; } $.ajaxFileUpload({ url : Util.getContentPath() + '/user/uploadHeaderPicTmp.do?inputId=realPicFile',//用于文件上传的服务器端请求地址 secureuri : false,//一般设置为false fileElementId : 'realPicFile',//文件上传空间的id属性 <input type="file" id="file" name="file" /> dataType : 'json',//返回值类型 一般设置为json success : function(data, status) //服务器成功响应处理函数 { // 图片在服务器上的相对地址,加随机数防止不刷新 path = Util.getContentPath() + data.path + "?" + Math.random(); $("#realPic").attr("src", path); var imgs = $(".jcrop-holder").find("img"); // 原本有图片,重新上传后,所有的img都需要刷新 imgs.each(function (i, v) { $(this).attr("src", path); }); $('#preview-pane .preview-container img').attr("src", path); // 切图样式 // Grab some information about the preview pane $preview = $('#preview-pane'), $pcnt = $('#preview-pane .preview-container'), $pimg = $('#preview-pane .preview-container img'), xsize = $pcnt.width(), ysize = $pcnt.height(); //console.log('init', [ xsize, ysize ]); $('#realPic').Jcrop({ onChange : updatePreview, //切图框改变事件 onSelect : updatePreview, // 切图框选择事件 onRelease: clearCoords, // 切图框释放的事件 bgFade : true, bgOpacity: .8, // 截图框以外部分的透明度 setSelect: [10, 10, 100, 100], // 默认选择的区域 aspectRatio : 1 //xsize / ysize 截图比例,这里我采用1 : 1 的比例,即切出来的为正方形 }, function() { // Use the API to get the real image size var bounds = this.getBounds(); boundx = bounds[0]; boundy = bounds[1]; // Store the API in the jcrop_api variable jcrop_api = this; // Move the preview into the jcrop container for css positioning $preview.appendTo(jcrop_api.ui.holder); }); }, error : function(data, status, e)//服务器响应失败处理函数 { Error(e); } }); return false; } function _getShowWidth(str) { return _getValue(str, "width"); } function _getShowHeight(str) { return _getValue(str, "height"); } function _getValue (str, key) { var str = str.replace(/\:|\;|\s/g, '').toLowerCase(); var pos = str.indexOf(key); if (pos >= 0) { // 截取 var tmp = str.substring(pos, str.length); var px = tmp.indexOf("px"); if (px > 0){ var width = tmp.substring(key.length, px); return width; } return 0; } return 0; } /** * 裁剪图片 */ function cutPic() { // 初始化数据 var x1 = $('#x1').val() == "" ? 0 : $('#x1').val(); var y1 = $('#y1').val() == "" ? 0 : $('#y1').val(); var x2 = $('#x2').val(); var y2 = $('#y2').val(); var w = $('#w').val() == "" ? 150 : $('#w').val(); var h= $('#h').val() == "" ? 150 : $('#h').val(); var srcFile = $("#realPic").attr("src"); if (srcFile == "" || !srcFile) { Error("没有选择任何图片."); return; } var showDiv = $(".jcrop-holder > .jcrop-tracker"); // 从压缩存放图片的div中获取压缩后显示的宽度和高度,用来交给后台同比例进行裁剪 // width: 404px; height: 304px; position: absolute; top: -2px; left: -2px; z-index: 290; cursor: crosshair; var style = showDiv.attr("style"); // 原图片页面显示的宽度 var showWidth = _getShowWidth(style); // 原图片页面显示的高度 var showHeight = _getShowHeight(style); // console.log(showWidth + " " + showHeight); // 原地图的src地址,去掉后边防止不刷新的随机数 srcFile = srcFile.substring(0, srcFile.indexOf("?")); $.ajax({ type : "post", dataType : "json", url : Util.getContentPath() + "/user/uploadHeaderPic.do", data : { srcImageFile : srcFile, x : x1, y : y1, destWidth : w, destHeight : h, srcShowWidth : showWidth, srcShowHeight : showHeight, }, success : function(data) { var okCallBack = function () { this.top.window.location = Util.getContentPath() + "/user/pcModiInfoInit.do"; }; var msg = eval(data); if(msg && msg.msg) if (msg.code == 1) Alert(msg.msg, okCallBack); else Error(msg.msg, okCallBack); else { Error("上传失败,请稍后重试.", okCallBack); return; } }, error : function () { Error ("上传失败,请稍后重试.") ; } }); } function clearCoords() { $('#coords input').val(''); };
@RequestMapping("uploadHeaderPicTmp") @ResponseBody public String uploadHeaderPic(String inputId, MultipartHttpServletRequest request) { try { MultipartFile realPicFile = request.getFile(inputId); InputStream in = realPicFile.getInputStream(); String path = request.getSession().getServletContext().getRealPath("/"); path += TMP; User loginUser = SystemUtil.getLoginUser(request.getSession()); String fileName = loginUser.getName() + "." + FilenameUtils.getExtension(realPicFile.getOriginalFilename()); File f = new File(path + "/" + fileName); FileUtils.copyInputStreamToFile(in, f); return "{\"path\" : \"" + TMP + "/" + fileName + "\"}"; } catch (Exception e) { LOG.error("upload header picture error : ", e); } return null; } @RequestMapping("uploadHeaderPic") @ResponseBody public GeneralMessage cutImage(String srcImageFile, int x, int y, int destWidth, int destHeight, int srcShowWidth, int srcShowHeight, HttpServletRequest request) { try { String path = request.getSession().getServletContext().getRealPath("/"); Image img; ImageFilter cropFilter; String srcFileName = FilenameUtils.getName(srcImageFile); // 读取源图像 File srcFile = new File(path + TMP + "/" + srcFileName); BufferedImage bi = ImageIO.read(srcFile); //前端页面显示的并非原图大小,而是经过了一定的压缩,所以不能使用原图的宽高来进行裁剪,需要使用前端显示的图片宽高 int srcWidth = bi.getWidth(); // 源图宽度 int srcHeight = bi.getHeight(); // 源图高度 if (srcShowWidth == 0) srcShowWidth = srcWidth; if (srcShowHeight == 0) srcShowHeight = srcHeight; if (srcShowWidth >= destWidth && srcShowHeight >= destHeight) { Image image = bi.getScaledInstance(srcShowWidth, srcShowHeight, Image.SCALE_DEFAULT);//获取缩放后的图片大小 cropFilter = new CropImageFilter(x, y, destWidth, destHeight); img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter)); BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); g.drawImage(img, 0, 0, null); // 绘制截取后的图 g.dispose(); String ext = FilenameUtils.getExtension(srcImageFile); path += HEADER_PIC; User loginUser = SystemUtil.getLoginUser(request.getSession()); String fileName = loginUser.getName() + "." + ext; File destImageFile = new File(path + "/" + fileName); // 输出为文件 ImageIO.write(tag, ext, destImageFile); loginUser.setPicPath(SystemConst.SYSTEM_CONTEXT_PATH_VALUE + HEADER_PIC + "/" + fileName); userService.update(loginUser); // 删除原临时文件 srcFile.delete(); GeneralMessage msg = new GeneralMessage(); msg.setCode(GeneralMessage.SUCCESS); msg.setMsg("上传成功!"); return msg; } } catch (Exception e) { e.printStackTrace(); } return null; }