import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
public class DownloadManager {
private static final int BUFFER_SIZE = 4096;
private String downloadUrl;
private String savePath;
private int numThreads;
private long fileSize;
private ConcurrentHashMap<Integer, Long> progressMap;
private ExecutorService executorService;
public DownloadManager(String downloadUrl, String savePath, int numThreads) {
this.downloadUrl = downloadUrl;
this.savePath = savePath;
this.numThreads = numThreads;
this.progressMap = new ConcurrentHashMap<>();
this.executorService = Executors.newFixedThreadPool(numThreads);
}
public void download() throws Exception {
URL url = new URL(downloadUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
fileSize = conn.getContentLengthLong();
conn.disconnect();
if (fileSize == -1) {
throw new Exception("Invalid URL or file not found");
}
File file = new File(savePath);
if (file.exists() && file.isFile()) {
long localFileSize = file.length();
if (localFileSize == fileSize) {
System.out.println("文件已下载完成。");
return;
} else if (localFileSize < fileSize) {
System.out.println("文件已存在,从上次下载的地方恢复下载 ...");
}
}
long blockSize = fileSize / numThreads;
for (int i = 0; i < numThreads; i++) {
long startPos = i * blockSize;
long endPos = (i == numThreads - 1) ? fileSize - 1 : startPos + blockSize - 1;
progressMap.put(i, startPos);
DownloadThread thread = new DownloadThread(i, startPos, endPos, url, savePath);
executorService.execute(thread);
}
executorService.shutdown();
while (!executorService.isTerminated()) {
Thread.sleep(100);
}
RandomAccessFile raf = new RandomAccessFile(savePath, "rw");
for (int i = 0; i < numThreads; i++) {
FileInputStream fis = new FileInputStream(getTempFilePath(i));
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = fis.read(buffer)) != -1) {
raf.seek(progressMap.get(i));
raf.write(buffer, 0, len);
progressMap.put(i, progressMap.get(i) + len);
}
fis.close();
new File(getTempFilePath(i)).delete();
}
raf.close();
System.out.println("文件下载完成。");
}
private String getTempFilePath(int threadId) {
return savePath + ".thread" + threadId;
}
private class DownloadThread implements Runnable {
private int threadId;
private long startPos;
private long endPos;
private URL url;
private String savePath;
public DownloadThread(int threadId, long startPos, long endPos, URL url, String savePath) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
this.url = url;
this.savePath = savePath;
}
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
InputStream inputStream = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(getTempFilePath(threadId), "rw");
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = inputStream.read(buffer)) != -1) {
raf.write(buffer, 0, len);
progressMap.put(threadId, progressMap.get(threadId) + len);
}
inputStream.close();
raf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
使用示例:
public static void main(String[] args) {
try {
DownloadManager downloadManager = new DownloadManager(
"http://example.com/file.zip", // 下载链接
"C:/Downloads/file.zip", // 本地存储路径
10 // 线程数
);
downloadManager.download();
} catch (Exception e) {
e.printStackTrace();
}
}
在上面的示例中,下载链接为 http://example.com/file.zip
,本地存储路径为 C:/Downloads/file.zip
,线程数为 10。程序将会自动计算并分配每个线程下载的部分,在下载过程中使用断点续传技术,自动在本地生成对应下载线程的临时文件,下载完成后合并临时文件到下载目标文件。