SpringMVC如何切片處理視訊大檔案
檔案上傳是最古老的網際網路操作之一,20多年來幾乎沒有怎麼變化,還是操作麻煩、缺乏互動、使用者體驗差。
一、前端程式碼
英國程式設計師Remy Sharp總結了這些新的介面,本文在他的基礎之上,討論在前端採用HTML5的API,對檔案上傳進行漸進式增強:
*iframe上傳 *ajax上傳 *進度條 *檔案預覽 *拖放上傳
1.1 傳統形式
檔案上傳的傳統形式,是使用表單元素file,參考 http://www.ruanyifeng.com/blog/2012/08/file_upload.html :
<formid="upload-form"action="upload.php"method="post"enctype="multipart/form-data"> <inputtype="file"id="upload"name="upload"/><br/> <inputtype="submit"value="Upload"/> </form>
所有瀏覽器都支援上面的程式碼,點選上傳按鈕後,網頁"鎖死",使用者只能等待上傳結束,然後瀏覽器重新整理,跳到表單的action屬性指定的網址。
1.2 iframe上傳
使用者點選submit時,動態插入一個iframe元素
var form = $("#upload-form");
form.on('submit',function() {
// 此處動態插入iframe元素
});
var seed = Math.floor(Math.random() * 1000);
var id = "uploader-frame-" + seed;
var callback = "uploader-cb-" + seed;
var iframe = $('<iframe id="'+id+'" name="'+id+'" >');
var url = form.attr('action');
form.attr('target', id).append(iframe).attr('action', url + '?iframe=' + callback);
1.3 ajax上傳
HTML5提出了XMLHttpRequest物件的第二版,從此ajax能夠上傳檔案了。這是真正的"非同步上傳",是將來的主流。
form.on('submit',function() {
// 此處進行ajax上傳
});
// 檢查是否支援FormData
if(window.FormData) {
var formData = new FormData();
// 建立一個upload表單項,值為上傳的檔案
formData.append('upload', document.getElementById('upload').files[0]);
var xhr = new XMLHttpRequest();
xhr.open('POST', $(this).attr('action'));
// 定義上傳完成後的回撥函式
xhr.onload = function () {
if (xhr.status === 200) {
console.log('上傳成功');
} else {
console.log('出錯了');
}
};
xhr.send(formData);
}
1.4 進度條
XMLHttpRequest第二版還定義了一個progress事件,可以用來製作進度條。
//在頁面中放置一個HTML元素progress
<progress id="uploadprogress" min="0" max="100" value="0">0</progress>
//定義進度progress事件的回撥函式
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var complete = (event.loaded / event.total * 100 | 0);
var progress = document.getElementById('uploadprogress');
progress.value = progress.innerHTML = complete;
}
}
二、後端
Spring框架中使用類似CommonsMultipartFile物件處理表二進位制檔案資訊,細心地會發現在利用框架下封裝的Multiform介面進行檔案上傳時,會先把檔案傳輸至tomcat一個指定的work目錄之下,然後再傳輸到指定的路徑。小檔案上傳這個時間延遲基本上可以忽略,但是在大檔案上傳時,這個上傳的速度就很讓人頭疼,上傳過程中的進度資訊無法訪問。
因此我們有必要從瀏覽器請求位元組流中解析Multiform協議,實現不依靠框架內建物件,取得使用者請求的所有資料,同時,使用者上傳的大小不受限制,而且在傳輸過程中,我們可以實時獲取傳輸進度。
參考https://www.cnblogs.com/darkprince/p/5114936.html
2.1普通Post請求協議及MultiPart協議
因為一次傳輸的大檔案MultiPart資料包,位元組數可能會很大(1G甚至以上),為了獲取實時進度資訊,以及記憶體開銷控制,我們需要將接收過程分成多段處理,即將資料包分段迴圈接收(例:每次迴圈只接收64K資料,期間即可更新當前的進度資訊)。本次我們採用Spring框架來實現“大檔案傳輸”功能,要點設計結構圖如下:
2.2原始碼解析
Filter物件:
用於負責接收MultiPart原始資料的Filter,用以在Spring內建物件之前接收使用者請求。需要在Web.xml中進行配置,Web啟動後,該Filter即啟動,當用戶請求到來時需要判斷該MultiPart資料資訊是否合法,接收並進行解析。
ServletInputStream/BufferedInputStream物件:
使用以上兩物件,可對本次請求進行按位元組流接收。在此可建立比較小的接收緩衝區,依靠BufferedInputStream的read進行分段迴圈接收。
getBoundarySectFromBuf()函式:
自定義函式,我們需要該函式從分段緩衝區中分析可能包含的多個Form表單資訊,或者部分表單資訊,或者二進位制檔案片段資訊。對於表單資訊分析後填充表單資料結構,對於二進位制檔案資訊需要寫檔案。該函式需要完成邊接收邊解析邊寫檔案的重要工作。
ProgressInfo物件:
進度資訊類,描述了一次上傳請求的進度資訊。該物件會用來被客戶端輪詢請求,以獲得當前傳輸大檔案過程中的進度資訊。
FormPart物件及listFormPart集合:
FormPart對於單個Form表單的描述。listFormPart為本次請求的全部表單描述集合。即供後續程式碼呼叫的全部表單項內容。
Controller層getProgInfo()處理函式:
該函式將接受來自瀏覽器的“獲得進度資訊請求”,並從當前ServletContext公共記憶體區中找到與ProgesssID對應的進度資訊物件ProgressInfo,以XML的形式返回給瀏覽器。該函式會被客戶端輪詢請求。
詳細程式碼可以參考:http://blog.ncmem.com/wordpress/2019/08/12/java實現大檔案上傳/
歡迎入群一起討論“374992201”