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;
}
注意fileinput-button这个样式,本人对css不熟悉,所以这个代码是从jquery-file-upload插件中拷贝过来的,大概的意思是将file的input放到按钮上,置为透明,这样就能够兼容IE,否则如果隐藏input,而触发它的click事件,IE认为这样做不安全,报拒绝访问的错误。
(2)、javascript关键代码:
//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('');
};
(3)springMVC代码:
@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;
}