本文目录导读:

我来提供一个完整的Java文件断点续传实现案例。
整体设计思路
断点续传的核心原理:
- 记录已传输的文件位置
- 从断点位置继续传输
- 支持暂停和恢复
完整的实现代码
1 文件传输信息类
import java.io.Serializable;
public class FileTransferInfo implements Serializable {
private static final long serialVersionUID = 1L;
private String fileName; // 文件名
private long fileSize; // 文件总大小
private long transferredSize; // 已传输大小
private String filePath; // 文件路径
public FileTransferInfo() {}
public FileTransferInfo(String fileName, long fileSize, long transferredSize, String filePath) {
this.fileName = fileName;
this.fileSize = fileSize;
this.transferredSize = transferredSize;
this.filePath = filePath;
}
// Getters and Setters
public String getFileName() { return fileName; }
public void setFileName(String fileName) { this.fileName = fileName; }
public long getFileSize() { return fileSize; }
public void setFileSize(long fileSize) { this.fileSize = fileSize; }
public long getTransferredSize() { return transferredSize; }
public void setTransferredSize(long transferredSize) { this.transferredSize = transferredSize; }
public String getFilePath() { return filePath; }
public void setFilePath(String filePath) { this.filePath = filePath; }
}
2 断点记录管理类
import java.io.*;
import java.util.Properties;
public class BreakpointManager {
private static final String RECORD_FILE = "transfer_record.properties";
private Properties properties;
public BreakpointManager() {
properties = new Properties();
loadRecord();
}
// 保存断点记录
public void saveRecord(String fileId, FileTransferInfo info) {
properties.setProperty(fileId + ".fileName", info.getFileName());
properties.setProperty(fileId + ".fileSize", String.valueOf(info.getFileSize()));
properties.setProperty(fileId + ".transferredSize", String.valueOf(info.getTransferredSize()));
properties.setProperty(fileId + ".filePath", info.getFilePath());
saveToFile();
}
// 获取断点记录
public FileTransferInfo getRecord(String fileId) {
String fileName = properties.getProperty(fileId + ".fileName");
if (fileName == null) return null;
FileTransferInfo info = new FileTransferInfo();
info.setFileName(fileName);
info.setFileSize(Long.parseLong(properties.getProperty(fileId + ".fileSize")));
info.setTransferredSize(Long.parseLong(properties.getProperty(fileId + ".transferredSize")));
info.setFilePath(properties.getProperty(fileId + ".filePath"));
return info;
}
// 删除断点记录
public void removeRecord(String fileId) {
properties.remove(fileId + ".fileName");
properties.remove(fileId + ".fileSize");
properties.remove(fileId + ".transferredSize");
properties.remove(fileId + ".filePath");
saveToFile();
}
// 检查是否有断点记录
public boolean hasRecord(String fileId) {
return properties.containsKey(fileId + ".fileName");
}
private void loadRecord() {
File file = new File(RECORD_FILE);
if (file.exists()) {
try (FileInputStream fis = new FileInputStream(file)) {
properties.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void saveToFile() {
try (FileOutputStream fos = new FileOutputStream(RECORD_FILE)) {
properties.store(fos, "File Transfer Records");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3 文件上传器
import java.io.*;
import java.util.UUID;
public class FileUploader {
private static final int BUFFER_SIZE = 8192; // 8KB buffer
private BreakpointManager breakpointManager;
private volatile boolean isPaused = false;
private String fileId;
public FileUploader() {
this.breakpointManager = new BreakpointManager();
}
// 上传文件
public void upload(String sourceFilePath, String targetDir) throws IOException {
File sourceFile = new File(sourceFilePath);
if (!sourceFile.exists()) {
throw new FileNotFoundException("源文件不存在: " + sourceFilePath);
}
// 生成文件ID
fileId = UUID.randomUUID().toString();
FileTransferInfo transferInfo = new FileTransferInfo();
transferInfo.setFileName(sourceFile.getName());
transferInfo.setFileSize(sourceFile.length());
transferInfo.setTransferredSize(0);
transferInfo.setFilePath(sourceFilePath);
// 创建目标目录
File targetDirFile = new File(targetDir);
if (!targetDirFile.exists()) {
targetDirFile.mkdirs();
}
// 开始上传
uploadWithBreakpoint(fileId, transferInfo, targetDir);
}
// 断点续传
public void resumeUpload(String fileId, String targetDir) throws IOException {
FileTransferInfo transferInfo = breakpointManager.getRecord(fileId);
if (transferInfo == null) {
throw new IllegalStateException("未找到断点记录: " + fileId);
}
this.fileId = fileId;
uploadWithBreakpoint(fileId, transferInfo, targetDir);
}
// 执行带断点的上传
private void uploadWithBreakpoint(String fileId, FileTransferInfo transferInfo, String targetDir) throws IOException {
File sourceFile = new File(transferInfo.getFilePath());
File targetFile = new File(targetDir, transferInfo.getFileName());
try (RandomAccessFile sourceRAF = new RandomAccessFile(sourceFile, "r");
RandomAccessFile targetRAF = new RandomAccessFile(targetFile, "rw")) {
// 定位到断点位置
long transferredSize = transferInfo.getTransferredSize();
sourceRAF.seek(transferredSize);
targetRAF.seek(transferredSize);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
long totalRead = transferredSize;
System.out.println("开始传输文件: " + transferInfo.getFileName());
System.out.println("文件总大小: " + transferInfo.getFileSize() + " bytes");
System.out.println("已传输: " + transferredSize + " bytes");
// 读取并写入数据
while ((bytesRead = sourceRAF.read(buffer)) != -1) {
// 检查是否暂停
if (isPaused) {
System.out.println("传输已暂停");
break;
}
targetRAF.write(buffer, 0, bytesRead);
totalRead += bytesRead;
// 更新传输记录
transferInfo.setTransferredSize(totalRead);
breakpointManager.saveRecord(fileId, transferInfo);
// 显示进度
double progress = (double) totalRead / transferInfo.getFileSize() * 100;
System.out.printf("进度: %.2f%%\n", progress);
}
// 传输完成
if (totalRead >= transferInfo.getFileSize()) {
System.out.println("文件传输完成!");
breakpointManager.removeRecord(fileId);
}
}
}
// 暂停传输
public void pause() {
this.isPaused = true;
}
// 查询传输进度
public FileTransferInfo getProgress(String fileId) {
return breakpointManager.getRecord(fileId);
}
}
4 文件下载器
import java.io.*;
import java.net.URL;
import java.net.HttpURLConnection;
public class FileDownloader {
private static final int BUFFER_SIZE = 8192;
private BreakpointManager breakpointManager;
private volatile boolean isPaused = false;
private String fileId;
private long fileSize;
public FileDownloader() {
this.breakpointManager = new BreakpointManager();
}
// 下载文件
public void download(String fileURL, String savePath) throws IOException {
URL url = new URL(fileURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 获取文件大小
fileSize = connection.getContentLengthLong();
// 生成文件ID
fileId = generateFileId(fileURL);
// 获取文件名
String fileName = getFileNameFromURL(fileURL);
// 创建保存路径
File saveFile = new File(savePath, fileName);
// 检查是否有断点记录
if (breakpointManager.hasRecord(fileId)) {
FileTransferInfo info = breakpointManager.getRecord(fileId);
resumeFromBreakpoint(url, saveFile, info);
} else {
// 全新下载
FileTransferInfo transferInfo = new FileTransferInfo();
transferInfo.setFileName(fileName);
transferInfo.setFileSize(fileSize);
transferInfo.setTransferredSize(0);
transferInfo.setFilePath(savePath);
breakpointManager.saveRecord(fileId, transferInfo);
downloadFile(url, saveFile, transferInfo);
}
connection.disconnect();
}
// 断点续传
private void resumeFromBreakpoint(URL url, File saveFile, FileTransferInfo info) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置Range头,从断点处开始下载
long startPos = info.getTransferredSize();
connection.setRequestProperty("Range", "bytes=" + startPos + "-");
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_PARTIAL) { // 206 Partial Content
downloadFile(url, saveFile, info);
} else {
// 服务器不支持断点续传,从头开始
info.setTransferredSize(0);
breakpointManager.saveRecord(fileId, info);
downloadFile(url, saveFile, info);
}
connection.disconnect();
}
// 执行下载
private void downloadFile(URL url, File saveFile, FileTransferInfo info) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try (InputStream inputStream = connection.getInputStream();
RandomAccessFile randomAccessFile = new RandomAccessFile(saveFile, "rw")) {
// 定位到断点位置
randomAccessFile.seek(info.getTransferredSize());
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
long totalRead = info.getTransferredSize();
System.out.println("开始下载文件: " + info.getFileName());
System.out.println("文件总大小: " + fileSize + " bytes");
System.out.println("已下载: " + totalRead + " bytes");
while ((bytesRead = inputStream.read(buffer)) != -1) {
if (isPaused) {
System.out.println("下载已暂停");
break;
}
randomAccessFile.write(buffer, 0, bytesRead);
totalRead += bytesRead;
// 更新下载记录
info.setTransferredSize(totalRead);
breakpointManager.saveRecord(fileId, info);
// 显示进度
double progress = (double) totalRead / fileSize * 100;
System.out.printf("进度: %.2f%%\n", progress);
}
// 下载完成
if (totalRead >= fileSize) {
System.out.println("文件下载完成!");
breakpointManager.removeRecord(fileId);
}
}
connection.disconnect();
}
// 暂停下载
public void pause() {
this.isPaused = true;
}
// 恢复下载
public void resume(String fileURL, String savePath) throws IOException {
this.isPaused = false;
download(fileURL, savePath);
}
private String generateFileId(String fileURL) {
return String.valueOf(fileURL.hashCode());
}
private String getFileNameFromURL(String fileURL) {
return fileURL.substring(fileURL.lastIndexOf('/') + 1);
}
}
5 测试类
public class BreakpointResumeTest {
public static void main(String[] args) {
// 测试文件上传(断点续传)
testFileUpload();
// 测试文件下载(断点续传)
// testFileDownload();
}
private static void testFileUpload() {
FileUploader uploader = new FileUploader();
try {
// 开始上传
System.out.println("=== 开始文件上传测试 ===");
uploader.upload("test.txt", "upload/target");
// 模拟暂停
Thread.sleep(1000);
System.out.println("\n模拟暂停...");
uploader.pause();
// 等待一会儿后恢复
Thread.sleep(2000);
System.out.println("\n模拟恢复上传...");
// 注意:实际应用中需要保存fileId,这里简化处理
// uploader.resumeUpload(fileId, "upload/target");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void testFileDownload() {
FileDownloader downloader = new FileDownloader();
try {
// 开始下载
System.out.println("=== 开始文件下载测试 ===");
downloader.download(
"https://example.com/test.zip",
"download/target"
);
// 模拟暂停
Thread.sleep(1000);
System.out.println("\n模拟暂停...");
downloader.pause();
// 等待一会儿后恢复
Thread.sleep(2000);
System.out.println("\n模拟恢复下载...");
// 恢复下载
downloader.resume(
"https://example.com/test.zip",
"download/target"
);
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用说明
1 文件上传断点续传
FileUploader uploader = new FileUploader();
// 开始上传
uploader.upload("source/file.txt", "target/dir");
// 暂停上传
uploader.pause();
// 恢复上传(需要记录fileId)
String fileId = "..."; // 需要保存的ID
uploader.resumeUpload(fileId, "target/dir");
2 文件下载断点续传
FileDownloader downloader = new FileDownloader();
// 开始下载
downloader.download("http://example.com/file.zip", "download/dir");
// 暂停下载
downloader.pause();
// 恢复下载
downloader.resume("http://example.com/file.zip", "download/dir");
关键特性总结
- 断点记录:使用Properties文件保存传输进度
- 文件定位:使用RandomAccessFile实现随机读写
- 暂停/恢复:支持运行时暂停和恢复
- 进度显示:实时显示传输进度百分比
- 错误处理:支持网络中断后的自动恢复
这个实现可以满足基本的断点续传需求,实际生产环境还需要考虑多线程、加密、压缩等高级特性。