小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

前端文件上傳基礎(chǔ)

 埃德溫會(huì)館 2017-02-14

文章來(lái)源

前端文件上傳基礎(chǔ)
作者:龍運(yùn)霞 2017-01-23 19:49
本篇文章僅限網(wǎng)易公司內(nèi)部分享,如需轉(zhuǎn)載,請(qǐng)取得作者本人同意授權(quán)

上傳文件已經(jīng)是個(gè)已經(jīng)成熟的前端技術(shù),目前開(kāi)源的拿來(lái)即用的前端上傳插件也比較多,諸如:Web Uploader、JSAjaxFIleUploaderjQuery-File-Upload,通常這些上傳插件包含的功能有:選擇上傳、支持拖拽、MD5校驗(yàn)、圖片預(yù)覽、上傳進(jìn)度顯示等功能; 這篇文章主要溫習(xí)一下前端上傳控件的功能實(shí)現(xiàn)原理,以及上傳功能如何做到功能的漸進(jìn)式增強(qiáng)。

文件上傳方式

文件上傳最原始的方式form元素表單提交,發(fā)展后form原始+iframe實(shí)現(xiàn)異步文件上傳,到后來(lái)HTML5出現(xiàn)ajax實(shí)現(xiàn)文件上傳。所以通常上傳控件向下兼容的方案通常是高版本瀏覽器采用ajax方式,低版本瀏覽器采用iframe+form表單形式。

form表單提交

<form id="j-puload-form" action="/fileUpload" method="post" enctype="multipart/form-data">    
    <input type="file" id="j-upload-input" name="upload"/><button type="submit">提交</button>
</form>

form表單屬性中action屬性規(guī)定后端處理文件上傳的路徑;method屬性規(guī)定上傳文件的方法post or get;enctype屬性規(guī)定在發(fā)送到服務(wù)器之前應(yīng)該如何對(duì)表單數(shù)據(jù)進(jìn)行編碼,在使用包含文件上傳控件的表單時(shí)必須使用“multipart/form-data”。 form表單提交

iframe封裝form表單

使用form元素比較簡(jiǎn)單,但缺點(diǎn)也比較明顯:上傳同步、上傳完成頁(yè)面會(huì)刷新; 在HTML5出現(xiàn)之前,想要實(shí)現(xiàn)文件異步上傳,只能通過(guò)iframe+form實(shí)現(xiàn);

實(shí)現(xiàn)方式

原理:文件上傳時(shí)在頁(yè)面中動(dòng)態(tài)創(chuàng)建一個(gè)iframe元素和一個(gè)form元素,并將form元素的target屬性指向動(dòng)態(tài)創(chuàng)建iframe元素。當(dāng)用戶完成選擇文件動(dòng)作時(shí),提交子頁(yè)面中的 form。這時(shí),iframe跳轉(zhuǎn),而父頁(yè)面沒(méi)有刷新。這使得上傳結(jié)束后,服務(wù)器處理結(jié)果返回到動(dòng)態(tài)iframe窗口而沒(méi)有刷新頁(yè)面;

<input type="file" id="j-upload-input" name="upload"/>
var createUploadForm = function (id, fileElementId) {  
      //create form    
    var formId = 'jUploadForm' + id;    
    var fileId = 'jUploadFile' + id;
    var form = $('<form  action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');    
    var oldElement = $('#' + fileElementId);    
    var newElement = $(oldElement).clone();    
    $(oldElement).attr('id', fileId);     
    $(oldElement).before(newElement);     
    $(oldElement).appendTo(form);    
    $(form).css('position', 'absolute');    
    $(form).css('top', '-1200px');    
    $(form).css('left', '-1200px');    
    $(form).appendTo('body');    
    return form;
}
var createUploadIframe = function (id) {    
//create frame    
var frameId = 'jUploadFrame' + id;    
var iframeHtml = '<iframe id="' + frameId + '" name="' + frameId + '" style="position:absolute; top:-9999px; left:-9999px"' + ' src="' + '" />';    
$(iframeHtml).appendTo(document.body);    
return jQuery('#' + frameId).get(0);
}
var actionURL = "/fileUpload";
$('#j-upload-input').change(function () {    
    var id = new Date().getTime() ;   
    var frameId = 'jUploadFrame' + id;    
    var formId = 'jUploadForm' + id;    
    var form = createUploadForm(id, "j-upload-input");
    var frame = createUploadIframe(id);   
    form.appendTo(document.body);   
    var form = $('#' + formId);    
    $(form).attr('action', actionURL);   
    $(form).attr('method', 'POST');    
    $(form).attr('target', frameId);    
    $(form).attr('enctype', 'multipart/form-data');    
    $(form).submit();
})

上述程序?qū)崿F(xiàn)了,id值為“j-upload-input”的input元素,在觸發(fā)文件選擇時(shí)(onchange事件),動(dòng)態(tài)創(chuàng)建一個(gè)form元素和一個(gè)iframe元素,input加入一個(gè)動(dòng)態(tài)創(chuàng)建form元素,并將form元素的target值指向iframe元素,最終結(jié)果實(shí)現(xiàn)了觸發(fā)input文件選擇,發(fā)送文件請(qǐng)求,但是頁(yè)面不刷新; 文件上傳不刷新

結(jié)果處理

通過(guò)iframe+form上傳,上傳結(jié)果處理需要前后端配合; 1.前后端預(yù)先約定好回調(diào)函數(shù)名; 例如,在當(dāng)前頁(yè)面中定義好上傳的回調(diào)函數(shù)。 function uploadCallBack (resp){...}

服務(wù)返回的數(shù)據(jù)形式可以為:

 <script type="text/javascript">
    window.top.window['uploadCallBack'](resp);
  </script>

通過(guò)window.top.window[uploadCallBack]可以調(diào)用到iframe父級(jí)元素中定義的uploadCallBack方法,也就是預(yù)先定義的回調(diào)處理; 2.前端頁(yè)可以監(jiān)聽(tīng)frame 的onLoad確定是否請(qǐng)求超時(shí)和后端是否給予返回;

通過(guò)FormData ajax方式

XMLHttpRequest Level 2添加了一個(gè)新的接口FormData利用FormData對(duì)象,我們可以通過(guò)JavaScript用一些鍵值對(duì)來(lái)模擬一系列表單控件,我們還可以使用XMLHttpRequest的send() 方法來(lái)異步的提交這個(gè)"表單"。比起普通的ajax,使用FormData 的最大優(yōu)點(diǎn)就是我們可以異步上傳一個(gè)二進(jìn)制文件。

構(gòu)建一個(gè)FormData并上傳文件

var xhr = new XMLHttpRequest();
var formData = new FormData();
for (var key in params) {    
    formData.append(key, params[key]);
}
formData.append(fileName, fileObj);
xhr.open(this.options.method, this.options.url, true);
xhr.send(formData);

通過(guò)拖拽操作選擇文件

現(xiàn)在很多上傳功能都包含拖拽上傳,實(shí)現(xiàn)上傳功能首先要?jiǎng)?chuàng)建一個(gè)拖放操作的目的區(qū)域并應(yīng)用程序的設(shè)計(jì)來(lái)決定哪部分的內(nèi)容接受 drop;

var dragArea;
if ((dragArea = document.getElementById("j-drag-area")) && dragArea.addEventListener) {    
    dragArea.addEventListener("dragover", dragoverHandler, false);    
    dragArea.addEventListener("dragleave", dragleaveHandler, false);    
    dragArea.addEventListener("drop", dropHandler, false);}

在例子中定義了id值為“j-drag-area”的元素為文件拖拽上傳受理區(qū)域,我們需要在該元素上綁定 dragover,dragleave,和drop 事件。 其中dragover,當(dāng)拖拽中的鼠標(biāo)移動(dòng)經(jīng)過(guò)一個(gè)元素的時(shí)候觸發(fā),可以做一些文件經(jīng)過(guò),拖拽區(qū)域高亮處理。dragleave當(dāng)拖拽中的鼠標(biāo)離開(kāi)元素時(shí)觸發(fā)。監(jiān)聽(tīng)器需要將作為可釋放反饋的高亮或插入標(biāo)記去除。drop 這個(gè)事件在拖拽操作結(jié)束釋放時(shí)于釋放元素上觸發(fā)。一個(gè)監(jiān)聽(tīng)器用來(lái)響應(yīng)接收被拖拽的數(shù)據(jù)并插入到釋放之地。

function dragoverHandler(event) {    
event.stopPropagation();   
 event.preventDefault();    
......
//這里可以添加拖拽區(qū)域背景高亮處理樣式
}
function dragleaveHandler(event) {    
event.stopPropagation();    
event.preventDefault();    
......
//這里可以異常拖拽區(qū)域背景高亮處理的樣式
}
function dropHandler(event) {   
 event.stopPropagation();   
 event.preventDefault();    
//獲取并處理文件
var dt = event.dataTransfer; 
var files = dt.files; 
handleFiles(files);
}

在代碼中的event.dataTransfer.files屬性表示被拖動(dòng)到瀏覽器窗口中的文件列表。

文件上傳進(jìn)度

XMLHttpRequest Level 2中,傳送數(shù)據(jù)的時(shí)候,有一個(gè)progress事件,上傳數(shù)據(jù)progress事件屬于XMLHttpRequest.upload對(duì)象,上傳數(shù)據(jù)過(guò)程中會(huì)觸發(fā)。事件回調(diào)函數(shù)中可以使用事件event的下列屬性:event.total是需要傳輸?shù)目傋止?jié);event.loaded是已經(jīng)傳輸?shù)淖止?jié);如果event.lengthComputable不為真,則event.total等于0。

var xhr = new XMLHttpRequest(),        
formData = new FormData();
xhr.onreadystatechange = function () {    
if (xhr.readyState == 4) {// 4 = "loaded"        
onComplete(xhr);//上傳完成處理    }};
xhr.upload.onprogress = function (e) {    
if (e.lengthComputable) {        
onProgressHandler( e.loaded, e.total, xhr);        
//e.total是需要傳輸?shù)目傋止?jié),e.loaded是已經(jīng)傳輸?shù)淖止?jié)。但如果e.lengthComputable值為false,則e.total等于0。       
// 通過(guò)(e.loaded/e.total)即可得到上傳比例,可以用這個(gè)已上傳比例去更新進(jìn)度條啦    
}
};
xhr.open(this.options.method, this.options.url, true);
for (var key in params) {    
formData.append(key, params[key]);
}
formData.append(fileName, fileObj);
xhr.send(formData);

對(duì)于低版本瀏覽器則可以用通過(guò)輪詢的方式獲取上傳進(jìn)度;

文件MD5

HTML5 DOM新增的File API,使得JavaScript操作文件成為可能;

File API 要在瀏覽器中對(duì)文件進(jìn)行md5,基本思路就是使用HTML5的FileReader接口把文件讀取到內(nèi)存,然后獲取文件的二進(jìn)制內(nèi)容,最后再進(jìn)行md5。 讀取文件

file = document.getElementById("file").files[0];

文件切割

//file的slice方法,注意它的兼容性,在不同瀏覽器的寫(xiě)法不同
blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice
//然后指定file和開(kāi)始結(jié)束的片段,就可以得到切割的文件了。
blobSlice.call(file, start, end);

計(jì)算文件MD5

spark = new SparkMD5();
spark.appendBinary(filepice1);
spark.appendBinary(filepice2);
spark.appendBinary(filepice3);
....//所有的分片處理好之后調(diào)用下面的方法就能獲取到文件的MD5了
spark.end()

附上js-spark-md5計(jì)算文件MD5方法 Demo源碼

document.getElementById('file').addEventListener('change',   function () { 
    var blobSlice = File.prototype.slice || File.prototype.mozSlice ||     File.prototype.webkitSlice, 
    file = this.files[0],
     chunkSize = 2097152, // Read in chunks of 2MB 
    chunks = Math.ceil(file.size / chunkSize),
     currentChunk = 0, 
    spark = new SparkMD5.ArrayBuffer(), 
    fileReader = new FileReader(); 
    fileReader.onload = function (e) { 
        console.log('read chunk nr', currentChunk + 1, 'of', chunks); 
        spark.append(e.target.result); // Append array buffer 
        currentChunk++;
         if (currentChunk < chunks) { 
            loadNext(); 
        } else {
             console.log('finished loading'); 
            console.info('computed hash', spark.end()); 
            // Compute hash
         } 
    }; 
    fileReader.onerror = function () { 
        console.warn('oops, something went wrong.');
     };
     function loadNext() {
         var start = currentChunk * chunkSize,
         end = ((start + chunkSize) >= file.size) ? file.size : start +    chunkSize;
         fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
     } 
    loadNext();
});

圖片預(yù)覽

如果上傳的文件是圖片類型,上傳插件通常會(huì)提供圖片預(yù)覽功能,圖片預(yù)覽首先要判斷文件類型是否為圖片類型,可以通過(guò)正則表達(dá)式匹配判斷

var imageType = /^image\//; 
if ( imageType.test(file.type) ) { 
    //是圖片;
 }

讀取和顯示圖片,首先要構(gòu)建一個(gè)img元素標(biāo)簽,給img的src屬性賦值;讀取圖片文件可用new FileReader()對(duì)象的readAsDataURL(file)方法,方法返回文件的base64編碼串。 例子: html

<input type="file" onchange="previewFile()"><br>
<img src="" height="200" alt="Image preview...">
function previewFile() { 
    var preview = document.querySelector('img'); 
    var file = document.querySelector('input[type=file]').files[0]; 
    var reader = new FileReader(); 
    reader.addEventListener("load", function () { 
        preview.src = reader.result; 
    }, false); 
    if (file) { 
    reader.readAsDataURL(file); 
    }
}

參考:

FormData Using XMLHttpRequest HTML5 file api 讀取文件MD5碼 文件上傳的漸進(jìn)式增強(qiáng) 在web應(yīng)用中使用文件 拖放操作 在瀏覽器端獲取文件的MD5值 js-spark-md5

42 16
譚雪微 , 宋俊剛 , 方向 , 劉伶令 , 祝娜 , 呂廣川魏天堯 , 韓平 , 王萃張祥潤(rùn) , 王艷尹紅煉 , 毛崯杰朱潔 , 鞠智寬 , 吳笛李仲浩 , 霍寶平 , 張大展 , 孫圣翔 , 劉詩(shī)川 , 陸建浩 , 方彬 , 張成 , yixin.libinbin , 蘇東樂(lè) , 謝壽保 , 孫飛王飛 , 吳碩碩 , 李福泉崔奇凡 , 張展宇 , 郝緒彤 , 沈洪梁胡兵心 , 周潔 , 劉一帝王夢(mèng) , 程霖 , 趙雨森朱立蕾
查看更多
...共42人
評(píng)論內(nèi)容
2017-02-08 16:06
回復(fù)
龍運(yùn)霞
回復(fù)孫圣翔:隨便轉(zhuǎn)發(fā)
2017-02-06 09:39
回復(fù)
孫圣翔
好全面的總結(jié),我可以轉(zhuǎn)發(fā)到外網(wǎng)嗎?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多