如何用Java案例实现文件断点续传?

wen java案例 5

本文目录导读:

如何用Java案例实现文件断点续传?

  1. 整体设计思路
  2. 完整的实现代码
  3. 使用说明
  4. 关键特性总结

我来提供一个完整的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");

关键特性总结

  1. 断点记录:使用Properties文件保存传输进度
  2. 文件定位:使用RandomAccessFile实现随机读写
  3. 暂停/恢复:支持运行时暂停和恢复
  4. 进度显示:实时显示传输进度百分比
  5. 错误处理:支持网络中断后的自动恢复

这个实现可以满足基本的断点续传需求,实际生产环境还需要考虑多线程、加密、压缩等高级特性。

抱歉,评论功能暂时关闭!