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

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

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

其實,上傳文件都是按照rfc1867標註來的, 只是分段上傳須要在前端多作點事情。分段上傳原理其實就是在前端使用JavaScript對文件進行分割成不一樣小塊,而後每次ajax請求就post一小塊,直到所有收到爲止。java

可是,爲了確保後端能判斷文件是否完整的收到,須要得知當前是第幾塊,一共多少塊,每一個分段的大小是多少(先後端同窗約定好吧),webuploader是有把這些參數傳給後端, 但文檔裏沒說明參數名稱啊,看了其餘後端example,而且本身作了實驗才知道。git


參數:
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

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

新整理的代碼放到github上了ajax

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

相關文章
相關標籤/搜索