記錄: 百度webuploader 分片文件上傳java服務器端(spring mvc)示例的優化


最近項目上用到文件分片上傳,於是找到了百度的一個開源前端控件webuploader。 於是嘗試使用。

下載下來后,它提供的服務器端示例代碼是php版的,那么Java版的呢?

其實,上傳文件都是按照rfc1867標注來的, 只是分段上傳需要在前端多做點事情。分段上傳原理其實就是在前端使用JavaScript對文件進行分割成不同小塊,然后每次ajax請求就post一小塊,直到全部收到為止。

但是,為了確保后端能判斷文件是否完整的收到,需要得知當前是第幾塊,一共多少塊,每個分段的大小是多少(前后端同學約定好吧),webuploader是有把這些參數傳給后端, 但文檔里沒說明參數名稱啊,看了其他后端example,並且自己做了實驗才知道。


參數:
name 文件名
chunks 一共有多少分片
chunk 當前分片是第幾片file 文件對象
重點來了:后端接收到這些參數應該怎么處理? 之前看過一個example ,是把每個分片文件都暫時存起來,命名為 文件名.part_1, 文件名.part_2, 文件名.part_3...文件名part_n, 每次都從1到總分片個數(注:這個值就是chunks的值),
遍歷這些文件是否存在.如果都存在說明全部上傳完成了,則再循環一遍,把所有分段都合並到一個文件里. 這么做雖然是可以,但是如果文件很大, 最后一個分片到達的時候響應可能會很慢,效率低下.
那么應該怎么解決呢?考慮了一會, 聯想到,以前使用迅雷之類的工具下載,除下載下來的文件以外,還會有一個額外的文件用來存放下載之類的信息.
受到這個啟發, 我決定這么設計: 每當第n個分片到達時(注:n的值其實就是收到的chunk的值), 使用java的隨機文件讀寫類RandomAccessFile, 定位到 n*分片大小(注:每個分片大小跟前端約定好的)的位置

long offset = chunkSize * param.getChunk();
//定位到該分片的偏移量
accessTmpFile.seek(offset);

然后寫入分片內容.
//寫入該分片數據
accessTmpFile.write(param.getFileItem().get());

同時,往一個配置文件,暫且命名為 文件名.conf 設置長度為chunks的值, 也就是分片個數.

accessConfFile.setLength(param.getChunks());

然后往第n個位置寫入一個Byte.MAX_VALUE,

accessConfFile.seek(param.getChunk());
accessConfFile.write(Byte.MAX_VALUE);

因為寫入的單位就是字節,所以我這么操作就相當於在第n個字節里寫入全1的狀態. 然后檢查,從0到chunks開始每一個字節進行與操作, 一旦到第n個字節發現與運算的結果不是全1(Byte.MAX_VALUE)那么就說明這個文件的第n個部分沒有傳輸完成.
如果conf文件0到chunks的位置全部進行與運算的最后結果還是Byte.MAX_VALUE,那么就說明這個文件已經傳輸完成,該干嘛就干嘛..
//completeList 檢查是否全部完成,如果數組里是否全部都是(全部分片都成功上傳)
byte[] completeList = FileUtils.readFileToByteArray(confFile);
byte isComplete = Byte.MAX_VALUE;
for (int i = 0; i < completeList.length && isComplete==Byte.MAX_VALUE; i++) {
//與運算, 如果有部分沒有完成則 isComplete 不是 Byte.MAX_VALUE
isComplete = (byte)(isComplete & completeList[i]);
System.out.println(prefix + "check part " + i + " complete?:" + completeList[i]);
}

if (isComplete == Byte.MAX_VALUE) {
System.out.println(prefix + "upload complete !!");
}
其實還有另一種想法是,前端傳來該文件的md5碼,然后后端每次接收到都算一次md5碼,如果一致則說明上傳成功,但是效率應該也不夠上面的好,於是沒實現. 誰有更好的想法可以留言哈.
 


現在可以開始例子:
在前端webuploader source的examples/image-upload/upload.js 中可以看到
        // 實例化
        uploader = WebUploader.create({
            pick: {
                id: '#filePicker',
                label: '點擊選擇圖片'
            },
            formData: {
                uid: 123
            },
            dnd: '#dndArea',
            paste: '#uploader',
            swf: '../../dist/Uploader.swf',
            chunked: false,
            chunkSize: 512 * 1024,
            server: '../../server/fileupload.php',
            // runtimeOrder: 'flash',

            // accept: {
            //     title: 'Images',
            //     extensions: 'gif,jpg,jpeg,bmp,png',
            //     mimeTypes: 'image/*'
            // },

            // 禁掉全局的拖拽功能。這樣不會出現圖片拖進頁面的時候,把圖片打開。
            disableGlobalDnd: true,
            fileNumLimit: 300,
            fileSizeLimit: 200 * 1024 * 1024,    // 200 M
            fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M
        });
chunked 被設置為false, 改為true就可以分片上傳了。
chunkSize這個后端需要用到,所以前后端需要保持一致。
server改成java后端自己定義的上傳文件接口的地址,我這里根據后端例子改成了“http://127.0.0.1:8080/file/test-upload2”

////此處已刪除一些舊的不可運行的代碼, 代碼請看分割線下面

 ----------------------------------------2018 分割線--------------------------------------------------

上面的代碼太久遠了, 從之前那公司項目里扣下來的, 忘記去掉某些不需要的類了` 在此抱歉

新整理的代碼放到github上了

https://github.com/ThomasHuang025/webuploader-spring-example

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM