大檔案下載/複製若支援斷點續傳,能大幅提升使用者體驗與系統健壯性。本文展示基於 RandomAccessFile 的多執行緒斷點續傳核心實現,關鍵在於每個執行緒獨立維護進度配置檔,重啟時讀取配置恢復斷點。
RandomAccessFile.seek(position) 定位讀寫指標。.config 檔,即時寫入當前檔案指標位置。package com.dragon.java.multithreaddownload;
import java.io.File;
public class Test {
public static void main(String[] args) {
File srcFile = new File("F:/mp4/01_Java.mp4");
int threadCount = 4;
if (!srcFile.exists()) {
System.out.println("來源檔案不存在!");
return;
}
File desFile = new File(srcFile.getParent(), "new_" + srcFile.getName());
long partLength = srcFile.length() / threadCount + 1; // 向上取整
for (int i = 1; i <= threadCount; i++) {
long start = partLength * (i - 1);
long end = partLength * i;
new MyThread(srcFile, desFile, start, end).start();
}
}
}
package com.dragon.java.multithreaddownload;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class MyThread extends Thread {
private File srcFile, desFile;
private long start, end;
public MyThread(File srcFile, File desFile, long start, long end) {
this.srcFile = srcFile;
this.desFile = desFile;
this.start = start;
this.end = end;
}
@Override
public void run() {
// 配置檔:以執行緒 ID 命名,避免衝突
File configFile = new File(desFile.getParent(),
Thread.currentThread().getId() + ".config");
// 若配置檔不存在但目標檔存在,視為已完成
if (!configFile.exists() && desFile.exists()) {
System.out.println(getName() + " 已完成下載");
return;
}
try (RandomAccessFile rafSrc = new RandomAccessFile(srcFile, "r");
RandomAccessFile rafDes = new RandomAccessFile(desFile, "rw");
RandomAccessFile rafConfig = new RandomAccessFile(configFile, "rw")) {
rafConfig.setLength(8); // long 佔 8 bytes
// 讀取上次斷點
long pointer = rafConfig.readLong();
if (pointer != 0) {
start = pointer; // 恢復斷點
}
rafSrc.seek(start);
rafDes.seek(start);
System.out.println(getName() + " 已下載:"
+ ((float) pointer / srcFile.length()) * 100 + "%");
byte[] buffer = new byte[1024]; // 1KB 緩衝區
int len;
while ((len = rafSrc.read(buffer)) != -1) {
rafDes.write(buffer, 0, len);
pointer = rafSrc.getFilePointer();
// 即時更新配置檔
rafConfig.seek(0);
rafConfig.writeLong(pointer);
// 到達本執行緒負責區段結束
if (pointer >= end) break;
}
} catch (IOException e) {
System.err.println(getName() + " 發生異常:" + e.getMessage());
} finally {
// 任務完成刪除配置檔
configFile.delete();
}
}
}
| 技術點 | 說明 |
|---|---|
RandomAccessFile | 支援隨機讀寫,seek() 移動指標,getFilePointer() 獲取當前位置 |
| try-with-resources | JDK 7+ 自動關閉流,簡化資源管理 |
| 配置檔設計 | 單一 long 值儲存指標,極輕量;以執行緒 ID 命名隔離 |
| 緩衝區大小 | 建議 1KB~8KB,平衡記憶體與系統呼叫頻率 |
| 完成判定 | 配置檔不存在 + 目標檔存在 = 已完成,避免重複工作 |
Range header) 實現真正的網路斷點續傳FileChannel + MappedByteBuffer 提升大檔案複製效能