捡捡java文件下载中的芝麻

来源:互联网 发布:约会交友app源码 编辑:程序博客网 时间:2024/05/17 01:52

前言

对于文件的下载上传是每个刚刚接触java的程序员都碰到的问题,而文件下载给与大家的印象就是简单,so easy。因为这些功能代码在网上一搜一大推,这点不得不说,度娘还是很强大的。在日常开发也是这样,遇到文件下载功能就直接从以前的老代码中copy过来,然后一阵键盘的敲击声。。。啪,功能实现了,这时候大部分程序员脸上都会露出满意的笑容!一副自己就是码神的自豪感油然而生。。。。因为下载功能经常使用,也就给大家造成很简单的印象。无非就是用流先读后写,是的是很简单,但是里面的一些小细节,也是我们需要注意或者学习的地方,否则有时候随着开发环境或者生产环境的不一样就会导致简单的下载功能都会出错!所以这里和大家一起捡捡文件下载中的小芝麻。

下载功能简单代码的展示

/** 1. 简单的文件下载小示例 2. @author 爱琴孩 */@Controller@RequestMapping("/DownloadDemo")public class DownloadDemo {        @RequestMapping("/downLoad")        public void downLoad(HttpServletRequest request, HttpServletResponse response) {            // TODO Auto-generated method stub            String filePath="F:/java学习资料/mysql笔记.doc";            File file = new File(filePath);            String fileName=file.getName();            try {                String userAgent = request.getHeader("user-agent").toLowerCase();                  if (userAgent.contains("msie") || userAgent.contains("like gecko") ) {                  // win10 ie edge 浏览器 和其他系统的ie                  fileName = URLEncoder.encode(fileName, "UTF-8");                  } else {                  // not ie                 fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");                  }            } catch (UnsupportedEncodingException e1) {                e1.printStackTrace();            }             response.setHeader("Content-Disposition", "attachment; filename=\""+ fileName);            response.addHeader("Content-Length", "" + file.length());            System.out.println("文件的大小是"+file.length());            response.setContentType("application/octet-stream;charset=UTF-8");            InputStream in = null;            OutputStream toClient = null;            try {                in = new BufferedInputStream(new FileInputStream(filePath));                byte[] buffer = new byte[in.available()];                System.out.println("字节数组的大小是"+buffer.length);                in.read(buffer);                in.close();                toClient = new BufferedOutputStream(response.getOutputStream());                toClient.write(buffer);                toClient.flush();            } catch (Exception e) {                e.printStackTrace();            } finally {                try {                    toClient.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            }}

上面的就是文件下载功能的小实现,想必大家也很熟悉了!这里想和大家一起总结一点小基础知识,如果觉得自己对上面的每一行代码都很熟悉的就直接绕路,因为大神不应该浪费时间在这些小芝麻上面!

由于是在本地测试,因此下载的文件也就是写死了,文件是存放在F盘中。文件名也就直接获取了,在实际开发过程中,文件的存放路径,或者文件名是存放在数据库中,在项目中在代码中获取到文件的路径,然后new有一个File,这里需要注意的是,

String userAgent = request.getHeader("user-agent").toLowerCase();  if (userAgent.contains("msie") || userAgent.contains("like gecko") ) {   // win10 ie edge 浏览器 和其他系统的ie      fileName = URLEncoder.encode(fileName, "UTF-8");   } else {      // not ie     fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");   }

由于文件名中对于中文汉字的编码问题,各个浏览器处理是不一样的,这就导致了有的浏览器对于下载下来的文件名会出现中文乱码的情况,所以需要利用上面的代码来进行判断控制,避免下载的文件文件名出现中文乱码情况!

response.setHeader("Content-Disposition", "attachment; filename=\""+ fileName);

对于设置Content-Disposition属性,这里是控制文件传输到浏览器的时候,会弹出一个框,来用用户选择下载还是直接打开,还有在浏览器上展示的文件名称,如果不设置就有可能会出现下载的文件会以默认的下载方法的名字来对下载的文件来进行命名。

response.addHeader("Content-Length", "" + file.length());

这句是用来设置请求或者响应对应内容的长度。至于为什么这里要设置这个Content-Length属性后面hi对应介绍。

response.setContentType("application/octet-stream;charset=UTF-8");

这里是设置以八进制流的性质来进行内容传输,对ContentType感兴趣的可以看看这篇文章,写的还不错,Http请求中Content-Type讲解以及在Spring MVC中的应用

至于BufferedInputStream和BufferedOutputStream,BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8M,能够减少访问磁盘的次数,提高文件读取性能;BufferedOutputStream是带缓冲区的输出流,能够提高文件的写入效率。

这里需要着讲解一下available()这个方法,available()是InputStream中的一个抽象方法,具体的实现需要看其子类的实现。像FileInputStream中的实现,他是用来读取流中文件的大小,然后生成对应的字节数组,用来保存文件,然后再通过流来写出。
这个方法用在从本地文件读取数据时,一般不会遇到问题,但如果是用于网络操作,就经常会遇到一些麻烦。比如,Socket通讯时,对方明明发来了1000个字节,但是自己的程序调用available()方法却只得到900,或者100,甚至是0。其实,这是因为网络通讯往往是间断性的,一串字节往往分几批进行发送。本地程序调用available()方法有时得到0,这可能是对方还没有响应,也可能是对方已经响应了,但是数据还没有送达本地。对方发送了1000个字节给你,也许分成3批到达,这你就要调用3次available()方法才能将数据总数全部得到。
在FileInputStream中已经重写了这个方法,重写之后的方法是通过文件的描述来获取文件的大小的。那么是怎么描述的呢,其实上面已经提到了

response.addHeader("Content-Length", "" + file.length());

这里设置了内容长度,所以available()在获取文件大小的时候就可以直接文件大小。上面代码的运行展示下载文件大小如下
这里写图片描述
这也佐证了上面的论述。
还有对于一些大文件的下载,这种方式是不行的,大文件采用分段方式,长连接的方式发送,不能一次知道文件大小,http 的header 里面就没有上面的content-length 属性,而是变成了Transfer-Encoding: chunked属性,这表示分段发送信息,但是对整个文件的接受,可以通过一些标志位,或者一些超时限制等方法处理,这里不具体研究了。

总结

上面都是些基础知识,有兴趣可以深入研究一下!

原创粉丝点击