今天做商城上传商品的的时候遇到一个需求,就是上传图片的时候需要通过多个上传图片域,上传不同的图片,以前因为项目进度很紧张,采取了一种应急措施:通过一个文件域上传,如下图:
之前用的是dropzone来上传图片,今天研究了良久,发现dropzone很难实现这样一个需求,于是换了一个上传图片插件:webuploader。
webuploader是一款由百度前端研发部开发的非常优秀的上传图片插件,采用大文件分片并发上传,支持常用图片格式jpg,jpeg,gif,bmp,png预览与压缩,节省网络数据传输,支持文件多选,类型过滤,拖拽(文件&文件夹),图片粘贴功能等功能。官网地址:http://fex.baidu.com/webuploader/
之后我们可以通过官网的一些例子快速上手这个插件,当我们通过官网的例子,可以做出类似如下的效果:
(图片那些是我自己加的,先不要管),当我们想要加入加入多个文件域的时候我们因该怎么做呢?
<div id="uploader"> <div id="queueListindex"> <span>首页图(最多1张图片)</span> <div id="dndArea" class="placeholder" style="margin-top: 20px"> <div id="filePicker"></div> <p>单次最多可选1张</p> </div> </div> <div id="queueListgalley"> <span>轮播图(最多3张图片)</span> <div id="dndArea1" class="placeholder" style="margin-top: 80px"> <div id="filePicker1"></div> <p>单次最多可选3张</p> </div> </div> <div id="queueListdescrip"> <span>详情图(最多4张图片)</span> <div id="dndArea2" class="placeholder" style="margin-top: 80px"> <div id="filePicker3"></div> <p>单次最多可选4张</p> </div> </div><div class="statusBar" style="display:none;"> <div class="progress"> <span class="text">0%</span> <span class="percentage"></span> </div> <div class="info"></div> <div class="btns"> <div id="filePicker2"></div> </div> </div> <div class="uploadBtn am-btn am-btn-primary"><input type="submit" class="am-btn am-btn-primary" value="开始上传"> </div>
</div>
在这里我们初始化了3个选择图片的布局域(id为queueListindex,queueListgalley,queueListdescrip),并且初始化了上传商品的按钮,之后我们需要在我们的js文件里面初始化控件并激活选择图片按钮
var $wrap = $('#uploader'), // 图片容器 $queueIndex = $('<ul class="filelist"></ul>') .appendTo($wrap.find('#queueListindex')), // 图片容器 $queueGalley = $('<ul class="filelist"></ul>') .appendTo($wrap.find('#queueListgalley')), // 图片容器 $queueDescrip = $('<ul class="filelist"></ul>') .appendTo($wrap.find('#queueListdescrip')), // 状态栏,包括进度和控制按钮 $statusBar = $wrap.find('.statusBar'), // 文件总体选择信息。 $info = $statusBar.find('.info'), // 上传按钮 $upload = $wrap.find('.uploadBtn'), // 没选择文件之前的内容。 $placeHolderIndex = $wrap.find('#dndArea'), $placeHolderGalley = $wrap.find('#dndArea1'), $placeHolderaDescrip = $wrap.find('#dndArea2'),
uploader = WebUploader.create({ pick: { //选择按钮 id: '#filePicker1', label: '点击选择图片', multiple: true }, formData: { //表单附带的可以写在这,如果只上传图片就可以不要 uid: uid }, threads:1, //同一时间只允许一个图片上传 dnd: '#dndArea', paste: '#uploader', swf: contextPath + '/resources/js/webUploader/Uploader.swf', //改成自己的Uploader.swf地址 chunked: false, chunkSize: 512 * 1024, duplicate:true, //是否允许重复的图片上传 server: contextPath + '/zhu/shelves', //接收表单的地址 runtimeOrder: 'flash', accept: { title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }, multiple: true, // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。 disableGlobalDnd: true, fileNumLimit: 15, fileSizeLimit: 20 * 1024 * 1024, // 200 M fileSingleSizeLimit: 10 * 1024 * 1024 // 50 M });
这里我们初始化了我们的控件,然后我们继续添加我们的剩下的上传按钮
uploader.addButton({ id: '#filePicker', innerHTML: '选择文件' }); uploader.addButton({ id: '#filePicker3', innerHTML: '选择文件' });
在这里我们可以添加我们的剩下的两个按钮,之后我们的3个文件域就差不多出来了,如下图:
在这个地方我遇到了一个问题,当我每次点击上传图片的时候,我需要确定我当前选定的这个文件域是那个文件域,但是当我用jq的on事件绑定我们选择文件按钮的控件的时候,我自己绑定的on会失效,找了下原因,因该是当我点击选择文件的时候会弹出上传文件,两个事件重复绑定造成的,所以我换了种思路,利用
var mybutton;//标志位 $('#filePicker1').on('mouseover', function () { mybutton = 1; }); $('#filePicker').on('mouseover', function () { mybutton = 0; }); $('#filePicker3').on('mouseover', function () { mybutton = 2; });
之后又有一个需求,我们在不同文件域的图片的最大数是不同的,例如:我们的queueListindex域里面就只允许有一张图片
翻了下webuploder api文档,有一个'beforeFileQueued'事件用于处理当我们的文件加入上传队列之前的操作。
uploader.on('beforeFileQueued', function (file) { if (mybutton == 0) { fileCountIndex++; //首页域的文件数量 if (fileCountIndex > 1) { return false; } } else if (mybutton == 1) { fileCountGalley++;//轮播域的文件数量 if (fileCountGalley > 3) { return false; } } else if (mybutton == 2) { fileCountDescrip++;//详情域的文件数量 if (fileCountDescrip > 4) { return false; } } });
在这个事件里面,我们通过当前的文件域来判断在当前这个文件域文件数达到最大值时则不能上传图片,如果不能,那么返回false,不让图片加入我们的队列。
当我们文件加入队列后,我们需要把加入队列的文件显示到我们的页面上,我们可以通过onFileQueued事件,处理在队列里面的图片
uploader.onFileQueued = function (file) { if (mybutton == 0) { fileSizeIndex += file.size; if (fileCountIndex === 1) { $placeHolderIndex.addClass('element-invisible');//把我们对应的上传域隐藏,之后我们会添加控件显示我们添加了的图片。 $statusBar.show(); } } else if (mybutton == 1) { fileSizeGalley += file.size; if (fileCountGalley === 1) { $placeHolderGalley.addClass('element-invisible'); $statusBar.show(); } } else if (mybutton == 2) { fileSizeDescrip += file.size; if (fileCountDescrip === 1) { $placeHolderaDescrip.addClass('element-invisible'); $statusBar.show(); } } addFile(file); //添加文件 setState('ready'); //设置状态,代码没贴出 updateTotalProgress();//更新进度,代码没贴出 };
function addFile(file) { var $li = $('<li id="' + file.id + '">' + '<p class="title">' + file.name + '</p>' + '<p class="imgWrap"></p>' + '<p class="progress"><span></span></p>' + '</li>'), //新建一个布局用于存放我们的图片 $btns = $('<div class="file-panel">' + '<span class="cancel">删除</span>' + '<span class="rotateRight">向右旋转</span>' + '<span class="rotateLeft">向左旋转</span></div>').appendTo($li), $prgress = $li.find('p.progress span'), $wrap = $li.find('p.imgWrap'), $info = $('<p class="error"></p>'), showError = function (code) { switch (code) { case 'exceed_size': text = '文件大小超出'; break; case 'interrupt': text = '上传暂停'; break; default: text = '上传失败,请重试'; break; } $info.text(text).appendTo($li); }; var suffix = file.name.split('.')[file.name.split('.').length - 1]; //记录文件后缀名 if (mybutton == 0) { file.name = "index" + i + "." + suffix;//给文件命名,根据前缀来区分是哪一类的图片 $li.appendTo($queueIndex); } else if (mybutton == 1) { file.name = "galley" + i + "." + suffix; $li.appendTo($queueGalley); } else if (mybutton == 2) { file.name = "describe" + i + "." + suffix; $li.appendTo($queueDescrip); } i++; }
当我们做到这一步的时候,其实已经可以上传图片到我们的服务器了,但是当我们需要连同表单一起上传进入服务器的话我们就需要uploadBeforeSend事件来处理:
uploader.on('uploadBeforeSend', function (obj, data) { //data.bbb="sadasd"; //alert(uid); data.uid = uid; data.goods_name = $("input[name='goods_name']").val(); data.type = $("input[name='type']").val(); data.price = $("input[name='price']").val(); data.goods_number = $("input[name='goods_number']").val(); mydiv = $('#my_select'); data.categoryId = $(mydiv.children('select')[mydiv.children('select').length - 1]).children('option:selected').val(); data.descri = $("input[name='descri']").val(); });
像这种格式,直接把data后面跟上参数名就可以给参数赋值。
$upload.on('click', function () { uploader.upload(); });
再通过 uploader.upload() 来上传我们的表单,之后的话图片和表单就可以上传进我们的服务器了,如下图:
在这里还没有完,因为我们的webuploder是采取分片上传图片的,每次只上传一张图片,不像dropzone一样,一次上传所有的图片,于是乎就涉及到一个问题,以前的服务器端的代码不能用了,所以我把服务器端的代码改了一下,适应我们的前端的需求。
在前端的话我们需要每次上传的时候添加个标识位:
var uid = 0;
之后我们把uid上传进服务器端
String myId = request.getParameter("uid"); if (myId.equals("0")){ //创建新的商品,并保存图片 }else{ //把图片保存进刚刚新建的商品里面 }
当我们服务器端收到第一张图片时会自动的生成一条商品信息,之后我们会返回我们刚刚生成的商品的id给我们的浏览器,上传成功,浏览器接收后
uploader.on('uploadSuccess', function (file, response) { uid = response.text; });
我们把返回的商品Id赋值给我们的标志位uid, 然后因为我们的webuploder是分片上传的,刚开始初始化时我们需要把threads设置为1,
threads: 1,
因为这样我们图片会等一张传完了后才开始上传第二张,所以我们就把我们返回的商品id上传给了我们的服务器,之后在服务器端判断后把图片存入对应的商品