最近太忙一直沒時間認真的寫博客(哈哈哈),最近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的方法,可是如今瀏覽器的支持率堪憂啊,因此只有曲線救國了。
我的知識的寬度和廣度畢竟有限,如文章有什麼疏漏和錯誤的地方,歡迎你們留言指出。