java实现高效文件下载

本文我们介绍几种方法下载文件。从基本JAVA IO 到 NIO包,也介绍第三方库的一些方法,如Async Http Client 和 Apache Commons IO.

最后我们还讨论在连接断开后如何恢复下载。

使用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系统上,这些方法使用零拷贝技术,减少了内核模式和用户模式之间的上下文切换次数。

使用第三方库

上面我们已经使用java 核心功能实现从URL下载文件。当无需调整性能是,我们也可以利用第三方库轻松实现。举例,在实际场景中,需要实现异步下载,我们可以封装逻辑至Callable,下面看已有库实现。

Async HTTP Client

Async HTTP Client是使用Netty框架执行异步HTTP请求的流行库。我们使用它对URL文件执行GET请求并获取其内容。首先需要创建HTTP client:

AsyncHttpClient client=Dsl.asyncHttpClient();

下面内容放到FileOutputStream:

FileOutputStream stream=new FileOutputStream(FILE_NAME);

接下来,创建HTTP GET请求并注册AsyncCompletionHandler 处理器去处理下载内容:

client.prepareGet(FILE_URL).execute(new AsyncCompletionHandler() {

@Override

public State onBodyPartReceived(HttpResponseBodyPart bodyPart)

throws Exception {

stream.getChannel().write(bodyPart.getBodyByteBuffer());

return State.CONTINUE;

}

@Override

public FileOutputStream onCompleted(Response response)

throws Exception {

return stream;

}

})

上面我们覆盖了onBodyPartReceived() 方法。缺省实现是累加HTTP 接收块至ArrayList,这可能导致耗费高内存或下载大文件时OutOfMemory 异常。

代替累加每个HttpResponseBodyPart 至内存,我们使用FileChannel写字节至本地文件。getBodyByteBuffer()方法通过ByteBuffer访问bodyPart内容。ByteBuffers的优势是把内存分配到JVM堆之外,所以不会影响应用程序的内存。

Apache Commons IO

另一个高可用的IO操作库是Apache Commons IO。我们从其Javadoc看到FileUtils实用类,用于一般的文件操作任务。从URL下载文件,仅需一行代码:

FileUtils.copyURLToFile(

new URL(FILE_URL),

new File(FILE_NAME),

CONNECT_TIMEOUT,

READ_TIMEOUT);

从性能角度看,与前面JAVA IO示例相同。底层代码使用相同的概念,从InputStream读取一些字节并将它们写入OutputStream。不同之处在于,URLConnection类在这里用于控制连接超时,这样下载就不会阻塞很长时间:

URLConnection connection = source.openConnection();

connection.setConnectTimeout(connectionTimeout);

connection.setReadTimeout(readTimeout);

恢复下载

考虑到internet连接的不确定性,失败时我们可以重新下载文件,但不是再次从字节0位置下载文件。

让我们重写前面的第一个示例,以添加这个功能。

我们首先要知道的是,我们可以使用HTTP HEAD方法从给定URL读取文件的大小,而无需实际下载它:

URL url = new URL(FILE_URL);

HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();

httpConnection.setRequestMethod("HEAD");

long removeFileSize = httpConnection.getContentLengthLong();

现在我们有了文件内容的总大小,可以检查文件是否已下载了部分内容。如果是,我们将继续从磁盘上记录的最后一个字节开始下载:

long existingFileSize = outputFile.length();

if (existingFileSize < fileLength) {

httpFileConnection.setRequestProperty(

"Range",

"bytes=" + existingFileSize + "-" + fileLength

);

}

上述代码我们配置了URLConnection以在一定范围内请求文件内容。范围将从最后下载的字节位置开始,并以远程文件大小的字节长度为结束。

使用范围HEAD标识的另一种常见方法是通过设置不同的字节范围以块形式下载文件。例如,要下载2KB文件,我们可以使用范围0 - 1024和1024 - 2048。

与前节代码稍微不同的是设置FileOutputStream 方法append参数为true:

OutputStream os = new FileOutputStream(FILE_NAME, true);

在我们做了这个更改之后,其余的代码与我们前面看到的代码一样。

总结

在本文中,我们已经看到Java中从URL下载文件的几种实现方式。

最常见的实现是在执行读/写操作时使用缓冲区。这个实现即使对于大文件也是安全的,因为我们没有将整个文件加载到内存中。

我们还了解了如何使用Java NIO通道实现零拷贝下载。这很有用,因为它最小化了在读取和写入字节时执行的上下文切换的次数,并且通过使用直接缓冲区字节不会加载到应用程序内存中。另外,由于下载文件通常是通过HTTP完成的,我们也说明如何使用AsyncHttpClient库实现这一点。

java 高效文件批量下载_java实现高效文件下载相关推荐

  1. 【java】 文件批量下载并压缩为zip压缩包

    [java] 文件批量下载并压缩为zip压缩包 java常用的压缩技术 java中常见实现压缩与解压 业务场景 代码实现 注意点 java常用的压缩技术 常见的压缩格式有很多种,例如:zip.rar. ...

  2. Java实现文件批量下载,打包成zip压缩包

       最近在做一个管理系统的项目,需要实现一个功能,就是批量下载文件,并打包成zip压缩包.    前端通过POST请求传来要下载的文件列表,Java代码实现如下: import java.io.Bu ...

  3. JAVA实现文件批量打包下载

    JAVA实现文件批量打包下载 实现 1.打包工具类的实现 /*** @author zhouxuan* @since 2019/4/19*/ public class ZipUtils {/*** @ ...

  4. java文件批量下载打包成zip

    1.首先html页面获取当前需要下载文件的id集合将转成字符串形式传递到后台. ................ 2 .下面就是获取字符串之后的批量下载/*** 多文件批量下载压缩成zip**root ...

  5. 多文件批量下载打包成.zip

    多文件批量下载打包成.zip //下载文件打包成zip压缩包@GetMapping("/zip/{id}")public void zip(@PathVariable(" ...

  6. java 解析m3u8的实例_使用java线程池批量下载m3u8。合并mp4.

    使用java线程池批量下载m3u8合并mp4. 看了线程池的demo,然后就想下载文件试试. 代码未必规范,多多建议. 大家可以自行修改,满足自己的需求. 还需要深入学习一下线程池. 给俺个星星⭐,可 ...

  7. 亿彩文件批量下载器 v3.0

    简介: 亿彩文件批量下载器,主要用于对已知的网络文件的Url,将其批量下载到本地上,并根据需要自动更名下载后的文件,功能丰富强大;支撑大容量几十万几百万条的下载,支持特殊网址的导入,支持保留网络路径结 ...

  8. JavaScript (mp3、mp4、jpg、doc、txt、rar)单个、多文件批量下载

    JavaScript 多文件下载 HTML5中 a 标签新增了一个属性 download,一般情况下,用户点击a链接浏览器会打开对应的链接地址,如果链接地址是一个文件如(xxx.rar.xxx.jpg ...

  9. Java多文件压缩下载解决方案

    Java多文件压缩下载解决方案 需求: 会员运营平台经过改版后页面增加了许多全部下载链接,上周上线比较仓促,全部下载是一个直接下载ZIP压缩文件的链接,每个ZIP压缩文件都是由公司运营人员将页面需要下 ...

最新文章

  1. P4269 [USACO18FEB]Snow Boots G
  2. 交通工程专业的计算机论文,交通工程(毕业论文).doc
  3. ngx_lua 模块
  4. DCMTK:OFpath和相关内容的测试程序
  5. 猪肉上的红章和蓝章有啥不同?| 今日趣图
  6. Sticks-hdu-1455深度搜索dfs
  7. python每秒20个请求_使用Python每秒百万个请求
  8. 程序员面试金典 - 面试题 16.11. 跳水板(数学)
  9. JavaScript学习系列之执行上下文与变量对象篇
  10. 流放之路进传送门显示服务器断线,资讯:特别改动 优化组队经验获取 降低断图风险...
  11. 总结之Unix的基础知识
  12. diskgenius分区linux选哪项,DiskGenius和和傲梅分区助手哪个好 无损C盘扩容选谁
  13. 人人商城小程序 用户登录授权接口 wx.getUserProfile后,个别用户出现无法登录的问题
  14. 和小白一起学习V4L2采集视频
  15. 模拟学信网登录,Cookie 序列化,在反序列化之后不能用的问题
  16. 圆周率:山颠一寺一壶酒
  17. cad模型轻量化_【技术帖】基于轻量化概念的碳纤维复合材料汽车保险杠设计
  18. 什么软件测试iphone性能,5款iPhone性能测试比拼:A9虽然垫底,与A13的差距并不大...
  19. 快速写出高质量IEEE论文的经验总结
  20. Ajax学习日志(三)—— 如何传递get请求参数

热门文章

  1. 母婴电商市场已成红海,贝贝网能否打破命运的藩篱?
  2. 甲方屡次选错乙方终致项目暴毙
  3. 【电脑硬件】电脑连不上网解决方法小结
  4. postgresql 常用基本命令
  5. xp计算机定时关机怎么批销,批处理 实现定时关机、注销、重启、锁定等功能
  6. Oracle RAC 11.2.0.4打PSU
  7. 【阿里淘宝天池Baseline服装搭配比赛】尝试一
  8. 淘宝穿衣搭配算法_方案三
  9. ldap安装linux,linux安装ldap-server
  10. 【物联网】行排式二维条码 介绍