斷點續傳下載的核心在於支援 HTTP Range 標頭,讓客戶端可指定下載位元組區間。下面提供一個基於 Servlet 的完整實現,支援瀏覽器與迅雷等多線程下載工具。
downloadFileRangesprivate void downloadFileRanges(File file) throws IOException {
long fileLength = file.length();
long pastLength = 0;
boolean isFlashGet = true; // true: 瀏覽器/閃獲;false: 迅雷等多線程
long lenEnd = 0;
String rangeHeader = request.getHeader("Range");
RandomAccessFile raf = null;
OutputStream out = null;
byte[] buffer = new byte[1024];
try {
// 1. 解析 Range 標頭
if (rangeHeader != null) {
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206
rangeHeader = rangeHeader.replaceAll("bytes=", "");
if (rangeHeader.endsWith("-")) { // bytes=100- 瀏覽器單線程
pastLength = Long.parseLong(rangeHeader.substring(0, rangeHeader.indexOf('-')));
} else { // bytes=100-200 迅雷多線程
isFlashGet = false;
String[] parts = rangeHeader.split("-");
pastLength = Long.parseLong(parts[0]);
lenEnd = Long.parseLong(parts[1]);
}
}
// 2. 回應標頭:宣告支援斷點續傳
response.setHeader("Accept-Ranges", "bytes");
if (pastLength > 0) {
String contentRange = isFlashGet
? "bytes " + pastLength + "-" + (fileLength - 1) + "/" + fileLength
: "bytes " + rangeHeader + "/" + fileLength;
response.setHeader("Content-Range", contentRange);
}
// 3. 設定下載檔名與內容類型
String fileName = new String(filename.getBytes("GBK"), "ISO8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.setContentType("application/octet-stream");
response.addHeader("Content-Length", String.valueOf(fileLength));
// 4. 核心傳輸邏輯
raf = new RandomAccessFile(file, "r");
out = new BufferedOutputStream(response.getOutputStream());
raf.seek(pastLength);
if (isFlashGet) {
int n;
while ((n = raf.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
} else {
while (raf.getFilePointer() < lenEnd) {
out.write(raf.read());
}
}
out.flush();
} catch (ClientAbortException e) {
log.debug("客戶端取消下載"); // 正常現象,忽略
} finally {
if (out != null) out.close();
if (raf != null) raf.close();
}
}
seek() 直接定位到斷點位置,避免讀取已下載資料。ClientAbortException(客戶端中斷),防止伺服器日誌汙染;迅雷會開啟多線程搶佔同一區塊,導致多個此異常,屬正常行為。GBK → ISO8859-1 轉碼相容大多數瀏覽器。<!-- struts.xml -->
<action name="downloadFile" class="downloadFileAction" method="downloadFile">
<result name="failed" type="redirectAction">showDownloadFileNameList</result>
</action>
<!-- JSP 下載連結 -->
<a href="downloadFile?filename=${fileMap.key}&filepath=${fileMap.value}">檔案下載</a>
此實現已在生產環境驗證,可支援大檔案、多線程下載工具與瀏覽器原生續傳功能。