chunkupload簡介javascript
chunkupload是一款基於java語言的斷點續傳組件,針對文件上傳,非文件下載,集成方便,使用簡單。html
chunkupload實現以下功能:java
· 實現斷點續傳web
· 對於同一個文件,容許多用戶同時上傳,而且上傳的用戶越多,上傳越快spring
· 線程安全chrome
· 同一物理機下進程安全api
· 文件自動切片,支持合併瀏覽器
· 內存佔用小緩存
· 高效穩定,高可用安全
· 易集成,無第三方依賴
chunkupload只關注文件上傳,並無安全機制,開發者須要自行設計安全控制策略,防範用戶上傳非法文件,chunkupload默認上傳的文件是安全的。
chunkupload功能完備,服務端和客戶端無縫銜接,開發者只需關注自身業務和UI展示便可。
爲了儘量提高用戶體驗,chunkupload在客戶端的技術選型有些激進,採用了許多先進的技術,好比:web worker、XMLHttpRequest數據傳送進度、FileReader、file slice等,因此對瀏覽器兼容性會有必定影響,在肯定使用chunkupload前請務必仔細斟酌!
chunkupload集成
服務端
chunkupload服務端運行須要JRE7或更高版本,無任何第三方依賴。
1.引用chunkupload.1.0.jar。
2.在項目web.xml中配置chunkupload servlet。
1 <servlet> 2 <servlet-name>ChunkUpload</servlet-name> 3 <servlet-class>com.iyangyuan.chunkupload.servlet.DispatcherServlet</servlet-class> 4 </servlet> 5 <servlet-mapping> 6 <servlet-name>ChunkUpload</servlet-name> 7 <url-pattern>/chunkupload/*</url-pattern> 8 </servlet-mapping>
強烈建議將chunkupload servlet配置在安全框架以後(好比shiro),mvc框架以前(如springmvc)。
chunkupload servlet中的攔截路徑(url-pattern),若是無特殊需求,無需更改,假如必定要更改,還須要同步更改客戶端的配置。
客戶端
chunkupload客戶端對瀏覽器的要求比較高,目前已知chrome、firefox瀏覽器完整支持,360瀏覽器若是啓用webkit內核,應該也沒有問題,IE瀏覽器絕對不支持(想都不要想),其餘瀏覽器未知。
開發者可能會問,爲何兼容性如此捉襟見肘?
chunkupload是新時代的產物,它表明了時代的發展方向,它的價值在於提供最早進的技術示範,而不是沉重的歷史包袱。
chunkupload在客戶端的實現,無任何第三方依賴,只關注與服務端的邏輯交互,並不干預UI展示,爲開發者創造最大的發揮空間。
1.引用dawn.1.0.js,用於在客戶端計算文件MD5,dawn.js是chunkupload的一個附屬項目,相比未經優化的javascript計算MD5方法,dawn.js將計算效率提高50%左右。
2.引用chunkupload.1.0.js,此乃chunkupload客戶端核心庫,封裝了全部上傳所需的邏輯。
chunkupload使用
服務端
服務端須要建立chunkupload.properties配置文件,放置在項目classpath根目錄下,也就是你們熟悉的log4j.properties所在目錄,配置文件中有以下選項:
· root 文件存儲路徑,至關於根目錄,內部還會有chunkupload建立的目錄結構;假如同一臺物理機配置多個文件上傳容器,此項配置應該設置成統一目錄,默認爲[/data]。
· fileLockCapacity 文件鎖緩存容量,通常設置爲2048便可,開發者可根據服務器性能自行調整,默認爲2048。
· createFile 文件上傳完成後,是否當即合併切片,生成完整文件;強烈建議此配置項設置爲false,通常狀況下,切片無需合併,就算合併,也不須要當即合併;若是設爲true,當即合併文件會佔用大量服務器資源,而且會形成客戶端長時間等待;合併的速度大約100M/S,視服務器具體性能而定,默認爲true。
至此,服務端已經能夠正常運做了。
客戶端
默認狀況下,客戶端無需任何配置。
假如開發者更改過chunkupload servlet攔截路徑,那麼chunkupload.1.0.js中的Block.config.api配置也須要作相應的改動,具體狀況須要開發者自行斟酌。
ChunkUpload 類
文件上傳核心實現類。
實例化
上傳組件初始化須要提供目標文件。
1 /** 2 3 * 實例化ChunkUpload組件 4 5 * file 要上傳的目標文件對象 6 7 */ 8 9 var cu = new ChunkUpload(file);
upload 方法
upload 方法用來啓動文件上傳,經過四個異步回調完成上傳交互,無返回值。
1 cu.upload({ 2 "success": function(block){ 3 /** 4 * 上傳成功回調 5 * 6 * block 對象,塊對象 7 */ 8 }, 9 "error": function(e){ 10 /** 11 * 上傳異常回調 12 * 13 * e 字符串,異常信息 14 */ 15 }, 16 "md5Progress": function(n){ 17 /** 18 * 計算文件md5進度回調 19 * 20 * n 整型,進度數值 21 */ 22 }, 23 "uploadProgress": function(n){ 24 /** 25 * 上傳進度回調 26 * 27 * n 整型,進度數值 28 */ 29 } 30 });
abort方法
abort方法用來中斷上傳,能夠在任意階段任意時刻中斷,無返回值。
1 cu.abort();
Block 類
文件控制類。
實例化
1 /** 2 3 * 初始化塊對象 4 5 * md5 文件md5,32位 6 7 * size 文件大小,字節 8 9 */ 10 11 var block = new Block(md5, size);
info方法
獲取塊(文件)信息,返回javascript對象。
1 block.info();
返回示例:
1 { 2 3 "status": 0, //業務狀態,0表示成功 4 5 "data": { //數據域 6 7 "chunks": [ //全部切片信息 8 9 { 10 11 "md5": "e114c21f7d9f8ad1a8551225c3d085be", //切片md5 12 13 "n": 1 //切片序號 14 15 }, 16 17 { 18 19 "md5": "48357caa7607a636e858315e1b0216d5", 20 21 "n": 2 22 23 }, 24 25 { 26 27 "md5": "a23c6ab7104d2ce4ae3c1624ea7eab55", 28 29 "n": 3 30 31 }, 32 33 { 34 35 "md5": "3eb29f6241d6fbb35cc715fff2b9ab91", 36 37 "n": 4 38 39 }, 40 41 { 42 43 "md5": "120ddc96b878a63adcd7835cbac0c95c", 44 45 "n": 5 46 47 } 48 49 ], 50 51 "chunkNum": 5, //切片數量 52 53 "md5": "f1154ca6fab7f3628927c1268f3570fd", //文件md5 54 55 "state": 1, //文件狀態,1爲上傳完成 56 57 "size": 20879935 //文件長度 58 59 } 60 61 }
delete方法
刪除塊(文件) ,無返回值。
1 block.delete();
chunkupload服務端存儲珠璣
任何上傳的文件都會在服務端進行切片處理,每一個切片4M大小。
經過文件MD5和文件大小,惟一肯定一個文件。
目錄分散策略,基於開發者自定義的rootpath,文件MD5前6位,每兩位做爲一級目錄,最後以文件MD5+文件長度做爲最終目錄,全部文件信息均存儲在此目錄下。
假如文件MD5爲[071287fffa974b878732a7a17858be36],長度爲[20879935],開發者自定義的rootpath爲[/data],那麼生成的目錄結構爲:[/data/07/12/87/071287fffa974b878732a7a17858be3620879935]。
chunkupload存儲的關於文件的全部信息,均爲二進制文件,而且文件名稱固定,具體組織以下圖:
chunkupload將來
展望chunkupload,將來無疑是開源的,只不過如今還不是時候,由於做者以爲它還不夠完美。
經過你們的寶貴意見、建議,做者會不斷完善、改進chunkupload,等到chunkupload成熟時,也就是開源之日!
但願你們多多與我交流~
chunkupload組件下載
你能夠下載以下內容:
· chunkupload.jar
· chunkupload.js
· dawn.js
· 腳手架(集成了chunkupload的空白項目)
附:客戶端使用示例
上傳示例
1 <html> 2 <head> 3 <title>ChunkUpload 文件上傳示例</title> 4 <meta charset="utf-8"> 5 <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> 6 <style> 7 body{ 8 font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; 9 } 10 body > div { 11 width: 200px; 12 margin: 20px auto 0 auto; 13 } 14 body > div.form { 15 text-align: center; 16 } 17 body > div.form > input { 18 margin-top: 12px; 19 border: 1px dashed #dcdcdc; 20 padding: 4px 8px; 21 cursor: pointer; 22 background-color: transparent; 23 color: #686868; 24 font-family: inherit; 25 outline: none; 26 width: 100%; 27 box-sizing: border-box; 28 } 29 body > div.form > input.button { 30 font-size: 15px; 31 line-height: 29px; 32 padding: 0 10px; 33 } 34 body > div.form > input:hover { 35 background-color: #f5f5f5; 36 } 37 div.info > div{ 38 margin: auto; 39 height: 31px; 40 width: 88px; 41 background: url('data:image/gif;base64,R0lGODlhWAAfAMMBAAAAAP////XcoPDLdfTboP+dzv+z2f+AwP9brfPZoAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFFAAKACwAAAAAWAAfAAAE/xDISau9OOvNd/hgKI5kaZ5oWkqB4L5wLM90bd+4PLB57//A2g7QChqPyNiwmGw6c8undKrj/STULC3qAxQAMKw0IUiYyy6yWf3j5rwGsMsrd5JfdzQelre5bxIFcROCdUBneWx6aHd9UFY1hAaTAJQTRo6LfYl8i0KQMoGCk6Slg0FsnGmdq3uZSpBiAnCmtaWGqK4yiq2eVURzwbS2pBK3hzabezOvLlxyYMPEg5aXWk1uWNLTxaOU10lR2oHckwcHlIWn4Ed/c97ECOfocJWy7EDus/C28udx8HAlaZYP1ItttRAgKEYBGS9NelL10LevnC2BR1RhM3iwAD9uGE5/NBvpg+IcLx5BhvTxiiDBGSYPivJI0xo+JzHfZfjyxNHDGC9/MaFhb0bRmzg5hoqE9EnOpk2XDJhKtarVq1izat3KtSvWDmDDih0LNgIAIfkEBRQACgAsAwAGADEAFgAABJ9QyEmrBMDqzbsFRTZh3pacZQUaogC2qZRQc4oVLLbCaS2XOpyBNdTxYjPf5iYcOp+kmCfqaj6vUClHlFlhr5is9nOpfsGAonGs6lrPUOvR1r3BnYdDEZemskdvWAh5ejs5fyqBV4N5LHKIiXcGCAhORnMmAkoUXpJgkEsFimeYPVsgond+oEtMoq86rJpTIZdBpbIjLBp9spu8W7nCshEAIfkEBRQACgAsAwAFADoAFgAABLdQyEmrAMDqzWXySdhZQJFN2KhS30oWxnmZrtp++IrB2B7XNmAPZogVezKgJidobXbEonSaUjaZE2erOpt6vz+gk5VFSTIlsBdDFTffnRM6qo4Bjkirbl4Hp6VJHTdxaD59BgcHRzx2ei5/dQiJiml3XI5xdGCSiTF0gZhPml8ICIB5oSqQh22pjwWjdaAuOWNjJCWwfZeuWoS5sME9rh4VtxolSMo0vXqWyGHESyOzZ9LHx9LaAhEAIfkEBRQACgAsAgAFAEQAFgAABMtQyEmrvTjrmmT63VYBgGieVIhSQFFO5CpPYWefrfEK7T5fto+g9iMVdKSc75dqOlWZ5NGgoyaXK6HqNvR4vxbjlEoux2ZQcHrL2uXK8DhWdGODo5LSO24GmJlqXSIvemN8Vn5ISVkaaRgxe4dwkXOAGpBGkmQHB1ZHfmeWRYZ8CJydSkiiTJGlpzqGlauDpHEICGRXsrMbrZq5THVPXcJhBbWSu2g0MmLIucona4BSx9YuoaKOzBbbMNi6RtHaRToXoLyO3nfneLw/EQAh+QQFFAAKACwCAAUASwAXAAAE3lDISau9OOtdE/9gKI4aAJDo5qUSUJyTyVJeYgtrfeesa8ACF5C1khSLuEkNZSr8TL4haKdMWpM8o/YDdRp+X6iUg9xer2VR0/ttu2Wh5bFKP5eAPrd+P55W00t2FjAneXtvAG9xGkhzHIRBbIdhiU9QM5gxhZKTb5J9mSAyXZ1tBwdhTolwoZiGkwinqFFPrZmvh7GnP5+haTSinHsICG1ioBdywFo8WRu4pcaZjslqBcKdyGQYv2ZcLtelrCS/3YKia9fql7btgy/HXdofZYHVTD8Xq+78FMjz/TBEAAA7') no-repeat center center; 42 padding: 2px; 43 box-sizing: border-box; 44 } 45 div.info > div > p{ 46 text-align: right; 47 font-size: 12px; 48 margin: 0; 49 } 50 </style> 51 </head> 52 <body> 53 <!-- 表單部分 --> 54 <div class="form"> 55 <input id="bigFile" type="file" placeholder="選擇一個文件" /> 56 <input id="execBtn" class="button" type="button" value="上傳" /> 57 <input id="cancelBtn" class="button" type="button" value="取消" /> 58 </div> 59 <!-- 上傳進度展現 --> 60 <div class="info"> 61 <div> 62 <!-- 提示文本 --> 63 <p id="text"></p> 64 <!-- 執行進度 --> 65 <p id="progress"></p> 66 </div> 67 </div> 68 </body> 69 <script src="/static/lib/dawn/dawn.js"></script> 70 <script src="/static/lib/chunkupload/chunkupload.js"></script> 71 <script> 72 var fileInput = document.getElementById("bigFile"), 73 execBtn = document.getElementById("execBtn"), 74 cancelBtn = document.getElementById("cancelBtn"), 75 text = document.getElementById("text"), 76 progress = document.getElementById("progress"), 77 cu; 78 79 execBtn.addEventListener("click", function(e) { 80 var file; 81 file = fileInput.files[0]; 82 cu = new ChunkUpload(file); 83 cu.upload({ 84 "success": function(block){ 85 /** 86 * 上傳成功後,會返回一個block對象 87 * 經過block對象,能夠在必定程度上管理文件,目前支持: 88 * 獲取文件信息(參見info_example.html) 89 * 刪除文件(參見delete_example.html) 90 */ 91 text.innerText = "上傳完成"; 92 }, 93 "error": function(e){ 94 text.innerText = e; 95 progress.innerText = ""; 96 }, 97 "md5Progress": function(n){ 98 text.innerText = "計算MD5"; 99 progress.innerText = n + "%"; 100 }, 101 "uploadProgress": function(n){ 102 text.innerText = "正在上傳"; 103 progress.innerText = n + "%"; 104 } 105 }); 106 }); 107 108 cancelBtn.addEventListener("click", function(e) { 109 if(cu){ 110 cu.abort(); 111 alert("已經取消!"); 112 } 113 }); 114 </script> 115 </html>
獲取文件信息示例
1 <html> 2 <head> 3 <title>ChunkUpload 獲取文件信息示例</title> 4 <meta charset="utf-8"> 5 <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> 6 <style> 7 body { 8 font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; 9 } 10 body > div { 11 width: 200px; 12 margin: 20px auto 0 auto; 13 } 14 body > div.form { 15 color: #686868; 16 font-size: 15px; 17 text-align: center; 18 } 19 body > div.form > p { 20 margin: 1em 0 .4em 0; 21 text-align: left; 22 } 23 body > div.form > input { 24 background-color: transparent; 25 outline: none; 26 font-family: inherit; 27 font-size: inherit; 28 color: inherit; 29 } 30 body > div.form > input:hover { 31 background-color: #f5f5f5; 32 } 33 body > div.form > input.text { 34 padding: 6px 8px; 35 border: 1px solid #dcdcdc; 36 } 37 body > div.form > input.button { 38 border: 1px dashed #dcdcdc; 39 cursor: pointer; 40 padding: 0 10px; 41 line-height: 29px; 42 } 43 div.info{ 44 width: 400px; 45 background-color: #f7faff; 46 border: 1px solid #b2d7ff; 47 padding: 10px 8px; 48 } 49 div.info > p{ 50 margin: 0; 51 text-align: left; 52 font-size: 14px; 53 word-break: break-all; 54 } 55 </style> 56 </head> 57 <body> 58 <!-- 表單部分 --> 59 <div class="form"> 60 <p>文件md5</p> 61 <input id="md5" class="text" type="text" placeholder="文件md5" /> 62 <p>文件大小(字節)</p> 63 <input id="size" class="text" type="text" placeholder="文件大小(字節)" /> 64 <p></p> 65 <input id="execBtn" class="button" type="button" value="獲取" /> 66 </div> 67 <!-- 結果展現 --> 68 <div class="info"> 69 <p id="text"> 70 </p> 71 </div> 72 </body> 73 <script src="/static/lib/dawn/dawn.js"></script> 74 <script src="/static/lib/chunkupload/chunkupload.js"></script> 75 <script> 76 var execBtn = document.getElementById("execBtn"), 77 md5 = document.getElementById("md5"), 78 size = document.getElementById("size"), 79 text = document.getElementById("text"); 80 81 execBtn.addEventListener("click", function(e) { 82 var block = new Block(md5.value, size.value); 83 84 block.info(function(info){ 85 text.innerText = JSON.stringify(info); 86 },function(status, text){ 87 text.innerText = "哎呀!出錯啦," + text; 88 }); 89 }); 90 </script> 91 </html>
刪除文件示例
1 <html> 2 <head> 3 <title>ChunkUpload 刪除文件示例</title> 4 <meta charset="utf-8"> 5 <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> 6 <style> 7 body { 8 font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; 9 } 10 body > div { 11 width: 200px; 12 margin: 20px auto 0 auto; 13 } 14 body > div.form { 15 color: #686868; 16 font-size: 15px; 17 text-align: center; 18 } 19 body > div.form > p { 20 margin: 1em 0 .4em 0; 21 text-align: left; 22 } 23 body > div.form > input { 24 background-color: transparent; 25 outline: none; 26 font-family: inherit; 27 font-size: inherit; 28 color: inherit; 29 } 30 body > div.form > input:hover { 31 background-color: #f5f5f5; 32 } 33 body > div.form > input.text { 34 padding: 6px 8px; 35 border: 1px solid #dcdcdc; 36 } 37 body > div.form > input.button { 38 border: 1px dashed #dcdcdc; 39 cursor: pointer; 40 padding: 0 10px; 41 line-height: 29px; 42 } 43 div.info{ 44 width: 400px; 45 background-color: #f7faff; 46 border: 1px solid #b2d7ff; 47 padding: 10px 8px; 48 } 49 div.info > p{ 50 margin: 0; 51 text-align: left; 52 font-size: 14px; 53 word-break: break-all; 54 } 55 </style> 56 </head> 57 <body> 58 <!-- 表單部分 --> 59 <div class="form"> 60 <p>文件md5</p> 61 <input id="md5" class="text" type="text" placeholder="文件md5" /> 62 <p>文件大小(字節)</p> 63 <input id="size" class="text" type="text" placeholder="文件大小(字節)" /> 64 <p></p> 65 <input id="execBtn" class="button" type="button" value="刪除" /> 66 </div> 67 <!-- 結果展現 --> 68 <div class="info"> 69 <p id="text"> 70 </p> 71 </div> 72 </body> 73 <script src="/static/lib/dawn/dawn.js"></script> 74 <script src="/static/lib/chunkupload/chunkupload.js"></script> 75 <script> 76 var execBtn = document.getElementById("execBtn"), 77 md5 = document.getElementById("md5"), 78 size = document.getElementById("size"), 79 text = document.getElementById("text"); 80 81 execBtn.addEventListener("click", function(e) { 82 var block = new Block(md5.value, size.value); 83 84 block.delete(function(info){ 85 text.innerText = JSON.stringify(info); 86 },function(status, text){ 87 text.innerText = "哎呀!出錯啦," + text; 88 }); 89 }); 90 </script> 91 </html>