1. 程式人生 > >【SSM】檔案下載的兩種方式

【SSM】檔案下載的兩種方式

檔案下載的兩種方式


今天專案中用到了一個檔案下載的方式,將一個app放在伺服器上,可以直接下載,剛開始想到了使用輸出檔案流的方式,思路有了,就直接上手寫了,參考網上的資料,整理出來了一個根據檔名,下載。後來同事一句話提醒了我,直接放在伺服器上,就能下載,開始感覺這個好像不行,怎麼能直接訪問這個呢?開玩笑吧???

後來腦海中一瞬間閃到一個畫面,就是除錯的時候,靜態資源的訪問,不是直接下載的js檔案嗎?直接訪問這個檔案試試呢,於是試了之後發現真的好使。

第一種:檔名和大小都是固定的

這種的就比較簡單了,可以直接將檔案放在專案的某一個資料夾下,簡單粗暴。然後在配置檔案中,將此資料夾配置成靜態資原始檔夾,這樣就可以直接訪問了。

一般是spring-mvc.xml檔案裡面,配置js,image等靜態資原始檔的地方

	<!-- 配置靜態資源的訪問對映,此配置中的檔案,將不被前端控制器攔截 -->
	<mvc:resources location="/static/" mapping="/static/**" />
	<mvc:resources location="/apk/" mapping="/apk/**" />

然後就可以直接在瀏覽器中直接訪問這個檔案就可以下載了。

http://192.168.17.31:8080/monitor-pay/apk/checkPay.apk

第二種:檔案有很多,檔名不固定

這種就可以利用輸出檔案流的方式了,在controller類裡面寫一個方法,接收引數為檔名,這樣就可以實現下載了。

    @RequestMapping(value="checkPay",method=RequestMethod.GET)
    public void download(HttpServletRequest request,HttpServletResponse response,String filename) throws IOException {
//checkPay.apk為需要下載的檔案 //String filename = "checkPay.apk"; //我這裡使用的是一個固定的檔案,方法可以不用寫filename引數 //獲取檔案的絕對路徑名稱,apk為根目錄下的一個資料夾,這個只能獲取根目錄資料夾的絕對路徑 String path = request.getSession().getServletContext().getRealPath("apk")+"\\"+filename; System.out.println(path); //得到要下載的檔案 File file = new File(path); if (!file.exists()) { response.setContentType("text/html; charset=UTF-8");//注意text/html,和application/html response.getWriter().print("<html><body><script type='text/javascript'>alert('您要下載的資源已被刪除!');</script></body></html>"); response.getWriter().close(); System.out.println("您要下載的資源已被刪除!!"); return; } //轉碼,免得檔名中文亂碼 filename = URLEncoder.encode(filename,"UTF-8"); //設定檔案下載頭 response.addHeader("Content-Disposition", "attachment;filename=" + filename); //1.設定檔案ContentType型別,這樣設定,會自動判斷下載檔案型別 response.setContentType("multipart/form-data"); // 讀取要下載的檔案,儲存到檔案輸入流 FileInputStream in = new FileInputStream(path); // 建立輸出流 OutputStream out = response.getOutputStream(); // 建立緩衝區 byte buffer[] = new byte[1024]; // 緩衝區的大小設定是個迷 我也沒搞明白 int len = 0; //迴圈將輸入流中的內容讀取到緩衝區當中 while((len = in.read(buffer)) > 0){ out.write(buffer, 0, len); } //關閉檔案輸入流 in.close(); // 關閉輸出流 out.close(); } }

剛開始是按照網上千變一律的寫法,BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());

不知道為什麼,用到我這裡下載速度很慢很慢,幾k,幾k的下載,並且手機瀏覽器下載還會出現中斷和下載不動的問題,失敗率幾乎百分百,PC端下載有那麼一兩次成功的,這就很不正常了,開始以為是客戶伺服器的原因,因為伺服器操作起來特別的卡頓。。。。。。

後來又查了些資料,借鑑這篇部落格裡的下載檔案的方式,添加了一個緩衝區,效果明顯的改善了,2.7M的apk秒下載那種。

關於緩衝區的作用,度娘上的這位大俠的解釋,很形象,但是緩衝區的大小問題,我還是沒明白:

終風且暴
就是這麼說吧,我打個比方
一個人要把水龍頭流出來的水弄到水缸裡面去,要是沒有緩衝池,每流出一滴水,你都要跑兩趟水龍頭與缸之間的距離(這個在傳檔案的時候就是磁碟讀寫的時間),而當你有一個緩衝池(比如盆),你可以等盆滿了再把水弄過去(這之間你可以做其他的事,在JAVA中,你就是CPU)……
所以有緩衝區的話,你可以節省CPU的大量時間,而且可以對緩衝區中的資料進行集中讀寫,這樣不必每來一個數據你去到磁軌上搜索地址,然後再回來接受資料,再去搜索地址存取資料,再回來接受資料。
緩衝區的大小根據你的使用者的上傳檔案的大小設定,一般取平均值,這個要經驗的。注意:緩衝區大小不是上傳檔案的平均值大小……


下載中文檔案時,需要注意的地方就是中文檔名要使用URLEncoder.encode方法進行編碼(URLEncoder.encode(fileName, "字元編碼")),否則會出現檔名亂碼。
修改這一句:

response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filename, "UTF-8"));