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

在Springboot中导出文件,按照上面写的还是中文乱码,则试下下面的方式:

fileName = URLEncoder.encode(fileName, "UTF-8");//postman测试中文文件名再次被编码,待前端测试是否正常
response.addHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + fileName);
response.setContentType("multipart/form-data");
out = response.getOutputStream();
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页