最近太忙一直没时间认真的写博客(哈哈哈),最近pm提一个需求,移动端须要一个上传图片的功能,容许多选、删除、预览、点击查看大图并能够滑动。虽然听起来不少,可是这个功能在web上实现过啊,使用webuploader妥妥的,而后就拍着胸口答应了下来,并让B同事作。javascript
开发完成后,后端说同一次上传多张图只能发一次请求,纳尼。。我没仔细看过webuploader的API,不知道为何须要单独进行上传,看了一下请求信息,在支持FormData的浏览器中使用的FormData来模拟form。那既然这样也能够支持多图一块儿请求啊(若是webuploader支持或者有其余考虑,欢迎你们指出),算了,本身造吧,也就有了这篇随笔。html
像我这样的小白都知道form表单提交以后要刷新页面,更别说要实现pm提的那些需求了,可是代码仍是要上一下的。java
<form action="http://baidu.com" target="" id="uploadForm" enctype="multipart/form-data"> <input id="file" type="file" name="file"/> <input type="submit" name="submit" id="submit" value="upload" /> </form>
由于咱们这里要上传文件,因此enctype的值为multipart/form-data。web
看到上面的代码,form有一个target的属性,规定在何处打开action,可能的值有ajax
就不一一介绍了,咱们最关心的是最后一个值,framename,咱们将页面放在iframe里处理就不担忧刷新的问题了,而后再设置一个回调就能够处理服务端返回的参数json
html:后端
<form action="http://baidu.com" target="" id="uploadForm" enctype="multipart/form-data"> <input id="file" type="file" name="file"/> <input type="submit" name="submit" id="submit" value="upload" /> </form>
jq:数组
<script type="text/javascript"> var form = $("#uploadForm"); form.on("submit",function(){ var seed = Math.floor(Math.random()*1000), id = "uploader-iframe" + seed, callback = "uploader-cb" + seed, iframe = $("<iframe id='"+id+"' name='"+id+"' style='display:none'></iframe>"), url = form.attr("action"); form.attr("target",id).append(iframe).attr("action",url+"?iframe="+callback); window[callback]=function(data){ iframe.remove(); form.removeAttr("target"); form.attr("action",url); window[callback] = undefined; } }) </script>
有没有以为和jsonp的方式有点像,可是这里不须要动态建立script标签来调用,由于iframe原本就是一个页面,只须要服务端返回调用方法和数据在iframe页面就ok了浏览器
服务端返回:缓存
<script type="text/javascript"> window.top.window[callback](data) </script>
callback是咱们事前约定好并传给服务器的参数,data为服务器返回的数据。
还有一种拿数据的方法,不经过后端回调
iframe.on("load",function(){ var ifr =$(this).contents() //jq对象document //ifr = this.contentDocument || this.document//兼容ie })
跟后端约定返回数据格式,而后进行操做
form+iframe这种伪异步的提交方式对文件的处理仍是无力,不能想删就删,预览图片只有先传给后台,后台再返回一个线上的地址
当当当。。文章的主角出现
利用FormData模拟表单数据,经过ajax进行提交,FileReader的readAsDataURL方法拿到base64地址来预览(完美,注意兼容性)
<form action="http://baidu.com" target="" id="uploadForm" enctype="multipart/form-data"> <input id="file" type="file" name="file"/> <input type="submit" name="submit" id="submit" value="upload" /> </form>
$.ajax({
url: '/upload',
type: 'POST',
cache: false,
data: new FormData($('#uploadForm')[0]),
processData: false,
contentType: false
}).done(function(res) {
}).fail(function(res) {});
processData
设置为false
。由于data
值是FormData
对象,不须要对数据作处理。<form>
标签添加enctype="multipart/form-data"
属性。cache
设置为false
,上传文件不须要缓存。contentType
设置为false
。由于是由<form>
表单构造的FormData
对象,且已经声明了属性enctype="multipart/form-data"
,因此这里设置为false。上传后服务端经过file来接收文件流。
<div id="uploadForm"> <input id="file" type="file"/> <button id="upload" type="button">upload</button> </div>
var formData = new FormData();
formData.append('file', $('#file')[0].files[0]);
$.ajax({
url: '/upload',
type: 'POST',
cache: false,
data: formData,
processData: false,
contentType: false
}).done(function(res) {
}).fail(function(res) {});
FileReader获取DataUrl
<input multiple="multiple" id="file" type="file" name="file"/>
var reader = new FileReader();
reader.onload=function(e){
//e.target.result为$("#file")[0].files[0]的base64地址
}
reader.readAsDataURL($("#file")[0].files[0])
更多FormData和FileReader方法能够去查看一下API
flash的实现不在咱们讨论的范围,并且浏览器的支持对flash有很大的影响,如今有不少上传组件作了低版本flash的兼容。好比webuploader、uploadify等
说了这么多,最后仍是要回归到需求上来,因为这个需求是在移动端上,咱们天然而然就选了第三种实现方式,这里讲一下实现的思路(伪代码)。
var formdata = new FormData(), count = 0, mId = '${model.id}', a = []; formdata.append("id", mId) $("#file").on("change",function() {//file触发change时循环files作相应的处理 if ($("#file")[0].files.length > 0) { for (var i = 0, j = $("#file")[0].files.length; i < j; i += 1) { (function(k) {//按顺序插入预览图片,并在数组中保存对应的files var reader = new FileReader() reader.onload = function(e) {//保证预览顺序和files数组顺序一致,方便后面删除file count += 1; a.push($("#file")[0].files[k]); $(".filelist").append("<li><p class='imgWrap'><img class='close' src='img/close.png'/><img id='" + $("#file")[0].files[k].name + "' class='choose-img' src='" + e.target.result + "'' /></p></li>");//预览相关处理 $(".swiper-wrapper").append("<div class='swiper-slide'><img id='" + $("#file")[0].files[k].name + "' class='choose-img' src='" + e.target.result + "'' /></div>");//点击查看大图相关处理 } reader.readAsDataURL($("#file")[0].files[i]); })(i); } } }); $(document).on("click", ".close", function() {//图片删除处理,处理files数组,更新count var $this = $(this); a.splice($this.parents("li").index(), 1); $(".swiper-slide").eq($this.parents("li").index()).remove();//若是考虑复用,这个index能够优化下 count -= 1; $this.parents("li").remove(); $("#jsUpload").show(); }) $("#jsUploadBtn").on("click", function() {//上传时,将files数组循环append进FromData进行ajax提交 for (var i = 0, j = a.length; i < j; i += 1) { formdata.append("file", a[i]) } $.ajax({ url : '', type : 'POST', cache : false, data : formdata, processData : false, contentType : false }).done(function(data) { if (data == 'error') { $(".flie-toast").addClass("hide"); toast("上传失败,请联系管理员!"); } else if (data == 'no') { $(".flie-toast").addClass("hide"); toast("您没有参与比赛,不容许上传截图"); } else if (data == 'yes') { $(".flie-toast").addClass("hide"); toast("上传成功"); setTimeout(function() { window.location.href = ""; }, 2000); } else { $(".flie-toast").addClass("hide"); toast("上传失败,请联系管理员!"); } }).fail(function(res) { }); })
虽然FormData对象有一个delete的方法,可是如今浏览器的支持率堪忧啊,因此只有曲线救国了。
我的知识的宽度和广度毕竟有限,如文章有什么疏漏和错误的地方,欢迎你们留言指出。