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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章