文件下載的兩種方式
今天項目中用到了一個文件下載的方式,將一個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的大量時間,而且可以對緩衝區中的數據進行集中讀寫,這樣不必每來一個數據你去到磁道上搜索地址,然後再回來接受數據,再去搜索地址存取數據,再回來接受數據。
緩衝區的大小根據你的用戶的上傳文件的大小設置,一般取平均值,這個要經驗的。注意:緩衝區大小不是上傳文件的平均值大小……