一、場景引入
上篇說到為了提高視頻並發播放的能力采用緩存 到內網的方式,因為視頻大小不同,但是也沒有特別大的視頻,所以我只把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);
}
}