springmvc图片上传、jquery 图片上传预览
宽为限 紧用功 功夫到 滞塞通
简介
项目需求,需要做图片上传功能,图片上传肯定得给个预览嘛,然后就找了下面这个方案
ajaxfileupload插件上传
ajaxfileupload.js网上传了好几个版本,选择自己可以使用的就好了。这个插件是N多年前写的了,估计后期作者没有继续维护,所以才出来多个版本吧,第一版的话现在最新jquery中使用的话会报错 handleError not find
ajaxfileupload现在来讲比较古老了,它以来jquery,而且是1.4.2版本的,那时候1.4.2版本的jQuery有handlerError方法,到了现在的1.9+就没有了,所以会报handlerError找不到的错误,解决方法是:找到ajaxfileupload.js文件,在 ” jQuery.extend({ “之后加上一下代码,就可以了!
所以在该文件中添加上 handleError函数
handleError: function (s, xhr, status, e) {
if (s.error) {
s.error.call(s.context || s, xhr, status, e);
}
if (s.global) {
(s.context ? jQuery(s.context) : jQuery.event).trigger("ajaxError", [xhr, s, e]);
}
},
前端页面代码
jsp代码
<tr>
<td style="text-align:right;">产品详情图:</td>
<td>
<input type="file" multiple="multiple" accept="image/gif,image/jpeg,image/png,image/jpg,image/bmp" id="chanpinzhutu" name="chanpinzhutu" onchange="setImagePreviews()" class="hide"/>
<label for="chanpinzhutu" class="labelBtn2">本地上传(最多选择6张)</label>
</td>
</tr>
<tr>
<td style="text-align:right;"></td>
<td>
<c:if test="${empty model.productPhotos['0'] }">
<img id="chanpinzhutu0" src="127.0.0.1:8080/test/images/empty_mid.png" width="80" height="80"/>
<img id="chanpinzhutu1" src="127.0.0.1:8080/test/images/empty_mid.png" width="80" height="80"/>
<img id="chanpinzhutu2" src="127.0.0.1:8080/test/images/empty_mid.png" width="80" height="80"/>
<img id="chanpinzhutu3" src="127.0.0.1:8080/test/images/empty_mid.png" width="80" height="80"/>
<img id="chanpinzhutu4" src="127.0.0.1:8080/test/images/empty_mid.png" width="80" height="80"/>
<img id="chanpinzhutu5" src="127.0.0.1:8080/test/images/empty_mid.png" width="80" height="80"/>
</c:if>
<c:if test="${not empty model.productPhotos['0'] }">
<c:forEach varStatus="i" var="img0" items="${model.productPhotos['0'] }">
<img id="chanpinzhutu${i.index }" src="${img0.imageLink }" width="80" height="80"/>
</c:forEach>
</c:if>
</td>
</tr>
js代码
// 展示详情图
function ajaxFileUploadMax() {
$.ajaxFileUpload ({
url: '127.0.0.1:8080/test/imgUploadMax?model=${model.fdId}', //用于文件上传的服务器端请求地址
secureuri: false, //是否需要安全协议,一般设置为false
fileElementId: 'chanpinzhutu', //文件上传域的ID
dataType: 'json', //返回值类型 一般设置为json
//服务器成功响应处理函数
success: function (data, status) {
//转换后的JSON对象
var obj = new Function("return" + data)();
var i = 0;
for(i; i < obj.length; i++){
//显示预览
$("#chanpinzhutu"+i).attr("src", obj[i]);
}
},
//服务器响应失败处理函数
error: function (data, status, e){
alert(e);
console.log(e);
}
})
return false;
}
springmvc后台代码
/**
* 多图片上传
* @param files
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value="/imgUploadMax", method=RequestMethod.POST)
@ResponseBody
public String imgUploadMax(@RequestParam(value = "chanpinzhutu", required = false) MultipartFile[] files,
HttpServletRequest request) throws Exception {
String modelId = request.getParameter("model");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dateNowStr = sdf.format(new Date());
String path = "/soluser/official/upload/product/" + dateNowStr;
JSONArray jsonArray = new JSONArray();
// 最多存6张
int fileSize = files.length > 6 ? 6 : files.length;
for (int i = 0; i < fileSize; i++) {
MultipartFile file = files[i];
String originalFilename = file.getOriginalFilename();
String substring = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileNameStr = (new Date().getTime()) + substring;
File targetFile = new File(path, fileNameStr);
if(!targetFile.exists()){
targetFile.mkdirs();
}
//保存
try {
file.transferTo(targetFile);
String imageLink = path + "/" + fileNameStr;
jsonArray.add(imageLink);
} catch (Exception e) {
e.printStackTrace();
}
}
return jsonArray.toString();
}
后台代码就是简单springmvc文件上传,MultipartFile 获取上传文件,保存并返回存储地址。
前端js获取后台传过来的保存地址,放到img标签上,然后展示上传的图片。
这里获取返回数据是遇到了问题,就是返回的data 在ajaxfileupload插件处理时报错了
我们看看它是如何处理返回数据的
uploadHttpData: function( r, type ) {
var data = !type;
data = type == "xml" || data ? r.responseXML : r.responseText;
// If the type is "script", eval it in global context
if ( type == "script" )
jQuery.globalEval( data );
// Get the JavaScript object, if JSON is used.
if ( type == "json" )
{
// If you add mimetype in your response,
// you have to delete the '<pre></pre>' tag.
// The pre tag in Chrome has attribute, so have to use regex to remove
/*var data = r.responseText;
var rx = new RegExp("<pre.*?>(.*?)</pre>","i");
var am = rx.exec(data);
//this is the desired data extracted
var data = (am) ? am[1] : ""; //the only submatch or empty
eval( "data = " + data );*/
// data=eval('(' + data+ ')'); // evaluate scripts within html
return data;
}
// evaluate scripts within html
if ( type == "html" )
jQuery("<div>").html(data).evalScripts();
//alert($('param', data).each(function(){alert($(this).attr('value'));}));
return data;
},
看if ( type == "json" )
这里我把它所有的处理都去掉了,因为我知道后台传递的数据所以这里就不需要特殊处理了。
略讲一下ajaxfileupload上传的原理,看createUploadIframe
和createUploadForm
,大概你就能猜到它的原理了。没错,它就是在页面上给你嵌套一个隐藏页面,页面里有个用于上传提交的Form,js代码将原来Form里的文件域里的信息放到隐藏页面里的Form中来,所以图片上传就被抽离出来了。
图片时展示了,但这是上传后再展示的,觉得有点别扭,要是后面还要修改的话岂不是服务器里要存一堆的图片!能不能先预览,等form表单提交时图片才保存呢? 于是有了第二种方案:
方案2
主表单没提交前就不保存图片,那么如果是ajax提交上来的图片后台就不给它保存,那前端这么显示预览呢?,对了,返回base64编码,浏览器可以解析base64编码的图片。然后后台就讲传过来的文件编码后再返回过去,嗯!感觉挺OK的,嘚瑟中…
后面想想这样是不是太傻逼了, 为什么不直接再前端就转码呢!(⊙o⊙)…
由于技术不足,然后又是各种找js base64编码方式,最后竟然没有一个成功的,,,解码的就随试随灵。(⊙﹏⊙)b,这个想法暂时放弃,然后突然记起以前学校时实现过图片即时浏览的功能,最后找到了它了 URL.createObjectURL
就这个东东,具体的看这里URL.createObjectURL和URL.revokeObjectURL
第二版代码
jsp代码
<tr>
<td style="text-align:right;">主页展示图:</td>
<td>
<input type="file" accept="image/gif,image/jpeg,image/png,image/jpg,image/bmp"
id="suoluetu" name="suoluetu" onchange="setImagePreviews1('suoluetu', 'showLittle')" class="hide"/>
<label for="suoluetu" class="labelBtnAdd">+</label>
<%-- 旧图片 --%>
<div id="oldImg1">
<c:if test="${not empty model.productPhotos['1'] }">
<c:forEach varStatus="i" var="img1" items="${model.productPhotos['1'] }">
<img id="suoluetu${i.index }" src="${img1.imageLink }" width="60" height="60" style="margin-left: 3px;"/>
</c:forEach>
</c:if>
</div>
<%-- 临时显示 --%>
<div id="showLittle"></div>
</td>
</tr>
js代码
/**
* 图片上传预览
* @param files
* @param show
* @returns
*/
function setImagePreviews1(files1, show1) {
// 编辑时隐藏之前的图片
$("#oldImg1").hide();
var docObj = document.getElementById(files1);
var dd = document.getElementById(show1);
dd.innerHTML = "";
var fileList = docObj.files;
var size = fileList.length > 6 ? 6 : fileList.length;
for (var i = 0; i < size; i++) {
dd.innerHTML += "<div style='float:left; margin-left:5px;' > <img id='imgOne" + i
+ "' /> </div>";
var imgObjPreview = document.getElementById("imgOne" + i);
if (docObj.files && docObj.files[i]) {
// 火狐下,直接设img属性
imgObjPreview.style.display = 'block';
imgObjPreview.style.width = '60px';
imgObjPreview.style.height = '60px';
// imgObjPreview.src = docObj.files[0].getAsDataURL();
// 火狐7以上版本不能用上面的getAsDataURL()方式获取,需要一下方式
imgObjPreview.src = window.URL.createObjectURL(docObj.files[i]);
} else {
// IE下,使用滤镜
docObj.select();
var imgSrc = document.selection.createRange().text;
var localImagId = document.getElementById("imgOne" + i);
// 必须设置初始大小
localImagId.style.width = "60px";
localImagId.style.height = "60px";
// 图片异常的捕捉,防止用户修改后缀来伪造图片
try {
localImagId.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)";
localImagId.filters
.item("DXImageTransform.Microsoft.AlphaImageLoader").src = imgSrc;
} catch (e) {
alert("您上传的图片格式不正确,请重新选择!");
return false;
}
imgObjPreview.style.display = 'none';
document.selection.empty();
}
}
return true;
}
图片选择时onchange="setImagePreviews1('suoluetu', 'showLittle')"
触发函数,这里还有个梗 input file里的JQ change() 事件的只生效一次 问题
这样图片浏览就是即时的了,提交时后台获取处理就OK了,这样服务器里就存一次正确的图片了,提交之前的图片统统还在前端,随浏览器的关闭而消失。就不用担心重复上传和无效文件上传问题了。O(∩_∩)O哈哈~
站在巨人的肩膀上
本篇博文参考的文章
http://www.cnblogs.com/kissdodog/archive/2012/12/15/2819025.html
http://docs.spring.io/spring/docs/4.0.x/javadoc-api/org/springframework/web/multipart/MultipartFile.html
http://www.cnblogs.com/ChenRunCheng/p/3592359.html
http://blog.csdn.net/imlinjunjie/article/details/49403199
http://www.mamicode.com/info-detail-456059.html
http://www.h3399.cn/201705/85905.html