SSM框架+Plupload實現斷點續傳(Spring+SpringMVC+MyBatis+Plupload)

關於Plupload的介紹,相信它的官網http://www.plupload.com/已經給得很詳細了。Plupload的上傳原理簡單點說,就是將用戶選中的文件(可多個)分隔成一個個小塊,依次向服務器上傳,這是它能駕馭上傳大文件的緣由之一,並且在這個過程能夠暫停上傳,暫停後再繼續上傳。最重要的是,從頭至尾沒有一點點UI阻塞,保證了用戶體驗。下面會開始講Plupload的實現流程,分析原理,並在最後給出效果圖。javascript


在此以前先說說個人項目,作的j2ee項目運用到Spring+SpringMVC+MyBatis的框架集合,是關於一個社交平臺的網站,相似於facebook,twitter,微博等,起了一個名字叫YouAndMe。我大膽地構想了這個項目應該有一個用戶資料共享的平臺,或是一部好看的電影,或是一套電視劇,或是居家必備的食譜,也有多是好看的風景圖,各式各樣。用戶能夠搜索想要的資料並下載。所以首先要解決的就是各式各樣(大)文件的上傳。css


一:下載Plupload插件並引入相應文件html

  1. 值得一提的是這個插件只是前端的,後臺怎麼獲取怎麼將一個個小塊合併等代碼是要本身寫的。前端

  2. 下載地址:http://www.plupload.com/download,我下的是Plupload 2.1.9 GPLv2版本的,裏面有要用到的css以及js。html5

  3. 在項目中須要引入:jquery.plupload.queue.css,jquery-2.0.0.min.js,plupload.full.min.js,jquery.plupload.queue.js,zh_CN.js這些文件。java


二:前端準備jquery

1.首先在html中寫入以下代碼:web

<div id="uploader"> <p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p> </div> <button id="toStop">暫停一下</button> <button id="toStart">再次開始</button>

注意div的id必須是uploader,這在插件源碼裏是有規定的;id爲toStop與toStart的按鈕是我本身加的,目的是爲了實現暫停上傳與暫停事後的繼續上傳。spring

2.頁面加載後經過js初始化組件服務器

<script type="text/javascript"> $(function() { // Initialize the widget when the DOM is ready var uploader = $("#uploader").pluploadQueue({ // General settings runtimes: 'html5,flash,silverlight,html4', url: "../pluploadUpload", // Maximum file size max_file_size: '10000mb', chunk_size: '1mb', // Resize images on clientside if we can resize: { width: 200, height: 200, quality: 90, crop: true // crop to exact dimensions }, // Specify what files to browse for filters: [ {title: "Image files", extensions: "jpg,gif,png"}, {title: "Vedio files", extensions: "mp4,mkv"}, {title: "Zip files", extensions: "zip,avi"} ], // Rename files by clicking on their titles rename: true, // Sort files sortable: true, // Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that) dragdrop: true, // Views to activate views: { list: true, thumbs: true, // Show thumbs active: 'thumbs' }, // Flash settings flash_swf_url: 'js/Moxie.swf', // Silverlight settings silverlight_xap_url: 'js/Moxie.xap' }); $("#toStop").on('click', function () { uploader.stop(); }); $("#toStart").on('click', function () { uploader.start(); }); }); </script>

關於這部分的功能能夠查看pluploadQueue的文檔:http://www.plupload.com/docs/pluploadQueue。也很容易看懂,這裏簡單地說說部分參數的意義。

url就是服務器處理該上傳的地址。filters是過濾器的意思,規定哪些格式的文件能夠上傳。dragdrop:true設置了能夠拖拽文件至選定框。

注意:在暫停與繼續上傳時要用到uploader.stop()uploader.start(),這個uploader實例由$("#uploader").pluploadQueue({...})時產生。在官網給出的例子中有兩種狀況:一是註冊時一次性所有給定參數,可是這樣是不會返回一個uploader實例的;二是註冊時不給參數,會返回uploader實例,再對這個uploader實例綁定事件時一步步給出參數。但很明顯我這裏給定了參數又同時返回了一個uploader實例,只要修改一個源碼:打開jquery.plupload.queue.js源碼找到定義pluploadQueue這塊,將if (settings) 內的返回return this,改爲return uploaders[$(this[0]).attr('id')]。這樣,點擊暫停按鈕時,當前上傳會暫停,點擊開始按鈕時,又繼續。


三:Controller映射

/**Plupload文件上傳處理方法*/ @RequestMapping(value="/pluploadUpload") public void upload(Plupload plupload,HttpServletRequest request,HttpServletResponse response) { String FileDir = "pluploadDir";//文件保存的文件夾 plupload.setRequest(request);//手動傳入Plupload對象HttpServletRequest屬性 int userId = ((User)request.getSession().getAttribute("user")).getUserId(); //文件存儲絕對路徑,會是一個文件夾,項目相應Servlet容器下的"pluploadDir"文件夾,還會以用戶惟一id做劃分 File dir = new File(request.getSession().getServletContext().getRealPath("/") + FileDir+"/"+userId); if(!dir.exists()){ dir.mkdirs();//可建立多級目錄,而mkdir()只能建立一級目錄 } //開始上傳文件 PluploadService.upload(plupload, dir); }

 

在這裏,我規定不一樣用戶上傳的資料會根據惟一id分開不一樣的文件夾,基於註釋代碼很容易看懂,你或許會困惑於Plupload與PluploadService,下面就會給出。


四:Plupload類

package web.plupload; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; /** * Plupload實體類固定格式,屬性名不可修改 * 由於MultipartFile要用到Spring web的依賴,而該依賴在web模塊中才引入,因此不把該實體類放在entity模塊 */ public class Plupload { /**文件原名*/ private String name; /**用戶上傳資料被分解總塊數*/ private int chunks = -1; /**當前塊數(從0開始計數)*/ private int chunk = -1; /**HttpServletRequest對象,不會自動賦值,須要手動傳入*/ private HttpServletRequest request; /**保存文件上傳信息,不會自動賦值,須要手動傳入*/ private MultipartFile multipartFile; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getChunks() { return chunks; } public void setChunks(int chunks) { this.chunks = chunks; } public int getChunk() { return chunk; } public void setChunk(int chunk) { this.chunk = chunk; } public HttpServletRequest getRequest() { return request; } public void setRequest(HttpServletRequest request) { this.request = request; } public MultipartFile getMultipartFile() { return multipartFile; } public void setMultipartFile(MultipartFile multipartFile) { this.multipartFile = multipartFile; } }
  1. 再次提醒類名與屬性名不可隨意改變。經過規定好的正確的屬性名,客戶端經過Http發送塊文件至服務端Controller,獲得的plupload對象才能傳入正確的文件信息。
  2. 關於屬性的說明代碼註釋已經說得很清楚了,能夠自行研究學習。

五:PluploadService類

package web.plupload; import org.springframework.util.MultiValueMap; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import java.io.*; import java.util.Iterator; import java.util.List; /** * Plupload Service模塊,同Plupload實體類同樣,由於要用到Spring web相關依賴,因此不將其放在Service模塊 */ public class PluploadService { public static void upload(Plupload plupload,File pluploadDir){ String fileName = ""+System.currentTimeMillis()+plupload.getName();//在服務器內生成惟一文件名 upload(plupload,pluploadDir,fileName); } private static void upload(Plupload plupload,File pluploadDir,String fileName){ int chunks = plupload.getChunks();//用戶上傳文件被分隔的總塊數 int nowChunk = plupload.getChunk();//當前塊,從0開始 //這裏Request請求類型的強制轉換可能出錯,配置文件中向SpringIOC容器引入multipartResolver對象便可。 MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)plupload.getRequest(); //調試發現map中只有一個鍵值對 MultiValueMap<String,MultipartFile> map = multipartHttpServletRequest.getMultiFileMap(); if(map!=null){ try{ Iterator<String> iterator = map.keySet().iterator(); while(iterator.hasNext()){ String key = iterator.next(); List<MultipartFile> multipartFileList = map.get(key); for(MultipartFile multipartFile:multipartFileList){//循環只進行一次 plupload.setMultipartFile(multipartFile);//手動向Plupload對象傳入MultipartFile屬性值 File targetFile = new File(pluploadDir+"/"+fileName);//新建目標文件,只有被流寫入時纔會真正存在 if(chunks>1){//用戶上傳資料總塊數大於1,要進行合併 File tempFile = new File(pluploadDir.getPath()+"/"+multipartFile.getName()); //第一塊直接從頭寫入,不用從末端寫入 savePluploadFile(multipartFile.getInputStream(),tempFile,nowChunk==0?false:true); if(chunks-nowChunk==1){//所有塊已經上傳完畢,此時targetFile由於有被流寫入而存在,要改文件名字 tempFile.renameTo(targetFile); } } else{ //只有一塊,就直接拷貝文件內容 multipartFile.transferTo(targetFile); } } } } catch (IOException e){ e.printStackTrace(); } } } private static void savePluploadFile(InputStream inputStream,File tempFile,boolean flag){ OutputStream outputStream = null; try { if(flag==false){ //從頭寫入 outputStream = new BufferedOutputStream(new FileOutputStream(tempFile)); } else{ //從末端寫入 outputStream = new BufferedOutputStream(new FileOutputStream(tempFile,true)); } byte[] bytes = new byte[1024]; int len = 0; while ((len = (inputStream.read(bytes)))>0){ outputStream.write(bytes,0,len); } } catch (FileNotFoundException e){ e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } finally { try{ outputStream.close(); inputStream.close(); } catch (IOException e){ e.printStackTrace(); } } } }

 

1.PluploadService這個類名是我本身起的,還能夠吧~

2.在Controller的最後一行PluploadService.upload(plupload, dir);中將客戶端提交至服務器生成的plupload對象與規定保存的文件夾目錄,以參數的形式傳入PluploadService的upload方法中。

3.在upload(Plupload plupload,File pluploadDir)方法中,爲文件生成一個惟一的文件名以便存儲在服務器中。

4.chunks是用戶一次性選中要上傳的文件中當前文件被分隔後的總塊數;nowChunk是此次上傳中塊的編號,從0開始,爲何用」此次「呢?前面提到過Plupload就是依次地將塊從客戶端提交至服務器,所以在文件上傳中,會有不少次Http請求,而同一個文件的chunks是不變的,nowChunk會一次次增長。

5.將HttpServletRequest強制轉換爲MultipartHttpServletRequest時可能會出錯,但這個錯誤能夠避免,只需在SpringIOC容器中注入一個名爲multipartResolver的對象

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- set the max upload size100MB --> <property name="maxUploadSize"> <value>104857600</value> </property> <property name="maxInMemorySize"> <value>4096</value> </property> <property name="defaultEncoding" value="UTF-8"></property> </bean>

 

6.經過MultipartHttpServletRequest拿到MultiValueMap(通過調試發現這個map只有一對鍵值對),其Value類型爲MultipartFile,這個MultipartFile其實就是當前的塊,也難怪爲何map中只有一個鍵值對了。

7.plupload.setMultipartFile(multipartFile);手動爲plupload對象傳入MultipartFile屬性值

8.若是總塊數chunks大於1,那就要考慮將上傳過來的一個個小塊合成一個文件,不然那就直接拷貝塊文件到目標文件multipartFile.transferTo(targetFile);

9.在chunks大於1時,首先要新建一個臨時文件tempFile,用於不斷不斷將一個個小塊寫入這個tempFile,等寫完後(chunks-nowChunk==1),就將其重命名(tempFile.renameTo(targetFile);)。

10.savePluploadFile(multipartFile.getInputStream(),tempFile,nowChunk==0?false:true);方法用於合併一個個小塊文件,若是是第一塊的話,就從頭開始寫入(new FileOutputStream(tempFile)),不然所有從末端寫入(new FileOutputStream(tempFile,true))。


寫到這裏,基於Plupload實現斷點續傳的代碼已經所有給出了,你們要本身整合至項目中,這裏沒有給出完整的demo,嗯仍是那句話,授之於魚不如授之以漁。


上傳效果圖(登陸了兩個用戶):

  1. 選定將要上傳的文件:

這裏寫圖片描述

2.開始上傳,有進度條顯示:

這裏寫圖片描述

3.暫停上傳:

這裏寫圖片描述

4.暫停後繼續上傳:

這裏寫圖片描述

5.上傳完畢:

這裏寫圖片描述

6.目標文件夾下有相應的文件:

這裏寫圖片描述

這裏寫圖片描述

7.上傳過程當中的網絡請求,體現斷點續傳:

這裏寫圖片描述


若有問題或補充,請你們在評論中儘管提,共同交流^~^。

相關文章
相關標籤/搜索