文件下载

一.使用java IO

下载文件最基本的方法是java IO,使用URL类打开待下载文件的连接。为有效读取文件,我们使用openStream() 方法获取 InputStream:

BufferedInputStream in = new BufferedInputStream(new URL(FILE_URL).openStream())

从InputStream读取文件时,强烈建议使用BufferedInputStream去包装InputStream,用于提升性能。
使用缓存可以提升性能。read方法每次读一个字节,每次方法调用意味着系统调用底层的文件系统。当JVM调用read()方法时,程序执行上下文将从用户模式切换到内核模式并返回。

从性能的角度来看,这种上下文切换非常昂贵。当我们读取大量字节时,由于涉及大量上下文切换,应用程序性能将会很差。

为了读取URL的字节并写至本地文件,需要使用FileOutputStream 类的write方法:

try (BufferedInputStream in = new BufferedInputStream(new URL(FILE_URL).openStream());
  FileOutputStream fileOutputStream = new FileOutputStream(FILE_NAME)) {
    byte dataBuffer[] = new byte[1024];
    int bytesRead;
    while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
        fileOutputStream.write(dataBuffer, 0, bytesRead);
    }
} catch (IOException e) {
    // handle exception
}

使用BufferedInputStream,read方法按照我们设置缓冲器大小读取文件。示例中我们设置一次读取1024字节,所以BufferedInputStream 是必要的。

上述示例代码冗长,幸运的是在Java7中Files类包含处理IO操作的助手方法。可以使用File.copy()方法从InputStream中读取所有字节,然后复制至本地文件:

InputStream in = new URL(FILE_URL).openStream();
Files.copy(in, Paths.get(FILE_NAME), StandardCopyOption.REPLACE_EXISTING);

上述代码可以正常工作,但缺点是字节被缓冲到内存中。Java为我们提供了NIO包,它有方法在两个通道之间直接传输字节,而无需缓冲。下面我们会详细讲解。

二.使用NIO
java NIO包提供了无缓冲情况下在两个通道之间直接传输字节的可能。

为了读来自URL的文件,需从URL流创建ReadableByteChannel :

ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream());

从ReadableByteChannel 读取字节将被传输至FileChannel:

FileOutputStream fileOutputStream = new FileOutputStream(FILE_NAME);
FileChannel fileChannel = fileOutputStream.getChannel();

然后使用transferFrom方法,从ReadableByteChannel 类下载来自URL的字节传输到FileChannel:

fileOutputStream.getChannel()
.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);

transferTo() 和 transferFrom() 方法比简单使用缓存从流中读更有效。依据不同的底层操作系统,数据可以直接从文件系统缓存传输到我们的文件,而不必将任何字节复制到应用程序内存中。

在Linux和UNIX系统上,这些方法使用零拷贝技术,减少了内核模式和用户模式之间的上下文切换次数。

三.javaweb实现文件下载(包含.txt文件等默认在浏览器中打开的文件)

jsp:

<a href="javascript:void(0)" id="zip" >src.zip</a><br><br>

js

$(document).ready(function(){
    
    $("#zip").click(function() {        
        location.href= path + "/device/downloadFile";
    });
    
    
});

后台代码

 @RequestMapping(value="/downloadFile")
     @ResponseBody
     public void downloadDeviceLog(HttpServletRequest request, HttpServletResponse response) throws Exception {
         String logUrl = "http://localhost:8080/dm/img/in.zip";
            try {
                String [] logUrlArray = logUrl.split("/");
                String fileName = logUrlArray[logUrlArray.length-1];
                URL url = new URL(logUrl);
                URLConnection uc = url.openConnection();
                response.setContentType("application/octet-stream");//设置文件类型
                response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                response.setHeader("Content-Length", String.valueOf(uc.getContentLength()));
                ServletOutputStream out = response.getOutputStream();
                IOUtils.copy(uc.getInputStream(), out);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
     }

  运行结果

注:

1、其中关键的一句是给响应头设置“content-disposition”属性,关于它的介绍可参考文章《Content-Disposition 响应头,设置文件在浏览器打开还是下载》

2、contenType也可以设置成专门针对某种文件类型的,比如文中是.txt类型,就可以这样设置:

response.setContentType("text/plain");//设置文件类型

3、最近将代码放在两台服务器上(两台tomcat编码相同),一台正常,一台报Server returned HTTP response code: 400 for URL,报错的地方在 IOUtils.copy(uc.getInputStream(), out);后来发现代码引用的url里面含中文,去掉中文之后发现好了,但是还是不知道为什么两台服务器会出现不一样的情况,先贴出来,以后找到问题原因再补充。
后边也可以自行设计代码copy

      OutputStream outp = null;
      FileInputStream in = null;
      try
      {
          outp = response.getOutputStream();
          in = new FileInputStream(filedownload); 
 
          byte[] b = new byte[1024];
          int i = 0;
 
          while((i = in.read(b)) > 0)
          {
              outp.write(b, 0, i);
          }
          outp.flush();
      }
      catch(Exception e)
      {
          System.out.println("Error!");
          e.printStackTrace();
      }
      finally
      {
          if(in != null)
          {
              in.close();
              in = null;
          }
          if(outp != null)
          {
              outp.close();
              out.clear();
              outp = null;
          }
      }
!
原文地址:https://www.cnblogs.com/lukelook/p/11066275.html