【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"));