在Java中实现多线程文件上传和下载可以提高文件传输的效率。
一、多线程文件上传
多线程文件上传通常包括以下步骤:
- 创建一个文件上传任务,将文件拆分成多个块,并为每个块创建一个线程进行上传。
- 在服务器端接收这些块并将它们组合成完整的文件。
- 确保所有线程都完成上传后才标记上传任务完成。
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
public class MultiThreadFileUpload {
public static void main(String[] args) {
String fileToUpload = "D:\path\file.txt";
String serverUrl = "http://localhost:8080/upload";
int numThreads = 4; // 设置线程数量
FileUploader[] uploaders = new FileUploader[numThreads];
try {
File file = new File(fileToUpload);
long fileSize = file.length();
long chunkSize = fileSize / numThreads;
for (int i = 0; i < numThreads; i++) {
long startByte = i * chunkSize;
long endByte = (i == numThreads - 1) ? fileSize - 1 : (i + 1) * chunkSize - 1;
uploaders[i] = new FileUploader(file, serverUrl, startByte, endByte);
uploaders[i].start();
}
// 等待所有线程完成上传
for (int i = 0; i < numThreads; i++) {
uploaders[i].join();
}
System.out.println("文件上传完成");
} catch (Exception e) {
e.printStackTrace();
}
}
}
class FileUploader extends Thread {
private File file;
private String serverUrl;
private long startByte;
private long endByte;
public FileUploader(File file, String serverUrl, long startByte, long endByte) {
this.file = file;
this.serverUrl = serverUrl;
this.startByte = startByte;
this.endByte = endByte;
}
@Override
public void run() {
try {
URL url = new URL(serverUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/octet-stream");
connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte);
FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.skip(startByte);
OutputStream outputStream = connection.getOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
fileInputStream.close();
outputStream.close();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_PARTIAL) {
System.out.println("上传成功: " + startByte + " - " + endByte);
} else {
System.out.println("上传失败: " + startByte + " - " + endByte);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
以上例子将文件分成多个块并使用不同的线程上传每个块,然后在服务器端将这些块组合成完整的文件。
二、多线程文件下载
- 获取要下载的文件的总大小。
- 创建多个线程,每个线程负责下载文件的一部分。
- 将所有下载的部分合并成完整的文件。
-
import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CountDownLatch; public class MultiThreadFileDownload { public static void main(String[] args) { String fileUrl = "http://localhost:8080/download/file.txt"; String destinationPath = "D:\path\file.txt"; int numThreads = 4; // 设置线程数量 try { URL url = new URL(fileUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); int fileSize = connection.getContentLength(); CountDownLatch latch = new CountDownLatch(numThreads); long chunkSize = fileSize / numThreads; for (int i = 0; i < numThreads; i++) { long startByte = i * chunkSize; long endByte = (i == numThreads - 1) ? fileSize - 1 : (i + 1) * chunkSize - 1; FileDownloader downloader = new FileDownloader(fileUrl, destinationPath, startByte, endByte, latch); new Thread(downloader).start(); } latch.await(); // 等待所有线程完成下载 System.out.println("文件下载完成"); } catch (Exception e) { e.printStackTrace(); } } } class FileDownloader implements Runnable { private String fileUrl; private String destinationPath; private long startByte; private long endByte; private CountDownLatch latch; public FileDownloader(String fileUrl, String destinationPath, long startByte, long endByte, CountDownLatch latch) { this.fileUrl = fileUrl; this.destinationPath = destinationPath; this.startByte = startByte; this.endByte = endByte; this.latch = latch; } @Override public void run() { try { URL url = new URL(fileUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte); InputStream inputStream = connection.getInputStream(); RandomAccessFile outputFile = new RandomAccessFile(destinationPath, "rw"); outputFile.seek(startByte); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputFile.write(buffer, 0, bytesRead); } inputStream.close(); outputFile.close(); latch.countDown(); // 通知主线程下载完成 System.out.println("下载完成: " + startByte + " - " + endByte); } catch (IOException e) { e.printStackTrace(); } } }
以上例子将文件分成多个块,并使用不同的线程下载每个块,然后将这些部分合并成完整的文件。注意使用
RandomAccessFile
来确保多线程下载时不会覆盖文件的其他部分。另外,使用CountDownLatch
来等待所有线程完成下载。