java 分片下载大文件避免堆内存溢出

一、场景引入

       上篇说到为了提高视频并发播放的能力采用缓存 到内网的方式,因为视频大小不同,但是也没有特别大的视频,所以我只把jvm的堆内存调整到1GB。然后采用整个视频下载到内存然后写到磁盘的方式(这种方法真的很蠢,而且隐

患巨大(☄⊙ω⊙)☄,不建议采用)。本以为这样做根本不会有任何问题,但是在测试人员进行测试的时候,一个大概430MB的视频在下载的时候堆内存就已经溢出了。故整个文件下载的做法不可取~!

二、解决方式

 

      既然整个视频下载会有内存溢出的隐患,那就采用分片下载的机制,何为分片下载?就是把一个视频分成若干份,每一份写到内存中然后再写到磁盘并释放内存空间,如此循环下去直到整个视频下载完毕!这样就不会出现堆内存溢出的问题。用

代码如何实现呢?

     分片下载最重要的部分就是开辟出一块固定内存,作为分片的大小,我声明的是一个5MB的字节数组:

byte[] buffer = new byte[1024 * 1024 * 5]; //5MB
(一定要小心,是5MB不是5GB。。。我写的时候一不小心多乘了一个1024就变成了5GB,然后程序一启动就爆了。。。)

声明完固定大小的字节数组,然后就可以将输入流inputStrem循环写入buffer中,如下所示:

/**
* 根据远程下载地址下载文件到本地
*
* @param downloadUrl 下载地址
* @param dir 保存本地地址
* @param fileName 文件名
* @return
*/
public static boolean downloadHttpUrl(String downloadUrl, String dir, String fileName) {
FileOutputStream fos = null;
InputStream inputStream = null;
try {
// 包含中文字符时需要转码
String patternUrl = encode(downloadUrl,"UTF-8");
URL url = new URL(patternUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 设置超时间为3秒
conn.setConnectTimeout(3 * 1000);

inputStream = conn.getInputStream();
//文件保存位置
File dirfile = new File(dir);
if (!dirfile.exists()) {
dirfile.mkdirs();
}
//写入到文件
fos = new FileOutputStream(dirfile + File.separator + fileName);
// 定义一个5M的字节数组 循环写入 避免把一次性写入内存把JVM撑爆
byte[] buffer = new byte[1024 * 1024 * 5];// 5MB
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
conn.disconnect();
return true;
} catch (Exception e) {
log.error("下载文件到本地错误:{}", e.getMessage());
return false;
} finally {
IOUtils.closeQuietly(fos);
IOUtils.closeQuietly(inputStream);
}
}





posted @ 2018-12-04 16:22  技术驱动产品  阅读(10381)  评论(0编辑  收藏  举报