You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中基于UrlConnection实现Zip文件断点续传方案咨询

用UrlConnection实现Zip文件断点续传方案

我来帮你搞定这个断点续传的问题!其实核心就是利用HTTP协议的Range请求头,让服务器从你上次中断的位置继续发送数据,下面一步步给你讲清楚实现思路和具体代码:

核心原理

断点续传的关键是HTTP的Range请求头,它允许客户端向服务器请求资源的某一段字节。当服务器支持这个特性时,会返回206 Partial Content状态码(而不是常规的200 OK),同时通过Content-Range响应头告诉客户端返回的字节范围和文件总大小。这样我们就能在本地已下载的文件基础上,继续追加后续的字节内容。

具体实现步骤

  1. 读取已下载进度:

    • 检查本地是否有未完成的下载文件,如果有,获取它的字节长度作为已下载进度;
    • 建议额外用一个临时文件(比如和下载文件同名的.download-progress文件)记录当前下载进度,这样即使应用意外关闭,下次启动也能准确读取进度(避免文件被篡改导致的进度不一致)。
  2. 配置Range请求头:

    • 建立UrlConnection连接后,设置请求头:connection.setRequestProperty("Range", "bytes=" + 已下载字节数 + "-"),这里的-表示从指定字节位置开始,一直到文件末尾。
  3. 处理服务器响应:

    • 如果响应码是206,说明服务器支持断点续传,直接读取输入流追加到本地文件;
    • 如果响应码是200,说明服务器不支持Range请求,只能从头开始下载;
    • 如果响应码是416,表示请求的范围无效(比如已下载的字节数超过了服务器上文件的总大小),这时候需要删除本地文件重新下载。
  4. 追加写入本地文件:

    • 打开文件输出流时,使用追加模式:new FileOutputStream(localFile, true),这样不会覆盖已有的下载内容。
  5. 保存进度与异常处理:

    • 每下载一定量的字节(比如每1MB)就更新一次进度文件;
    • 捕获网络中断、IO异常等情况,在异常发生时及时保存当前进度,避免进度丢失。

完整代码示例

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

public class ZipResumeDownloader {
    // 替换成你的下载链接
    private static final String DOWNLOAD_URL = "http://your-server.com/target-file.zip";
    // 本地保存路径
    private static final String LOCAL_ZIP_PATH = "/your/local/path/target-file.zip";
    // 进度记录文件
    private static final String PROGRESS_FILE = LOCAL_ZIP_PATH + ".download-progress";

    public static void main(String[] args) {
        File zipFile = new File(LOCAL_ZIP_PATH);
        File progressFile = new File(PROGRESS_FILE);

        long downloadedBytes = 0;

        // 读取已下载进度
        if (progressFile.exists()) {
            try (BufferedReader progressReader = new BufferedReader(new FileReader(progressFile))) {
                downloadedBytes = Long.parseLong(progressReader.readLine());
                // 校验本地文件大小和进度记录是否一致,防止文件被篡改
                if (zipFile.exists() && zipFile.length() != downloadedBytes) {
                    downloadedBytes = zipFile.length();
                    updateProgress(downloadedBytes, progressFile);
                }
            } catch (IOException e) {
                System.err.println("读取进度文件失败,将从头开始下载");
                downloadedBytes = 0;
            }
        } else if (zipFile.exists()) {
            // 没有进度文件但有已下载的文件,用文件长度作为进度
            downloadedBytes = zipFile.length();
            try {
                updateProgress(downloadedBytes, progressFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        try {
            URL url = new URL(DOWNLOAD_URL);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            // 设置Range请求头,请求从已下载位置开始的剩余内容
            connection.setRequestProperty("Range", "bytes=" + downloadedBytes + "-");
            connection.connect();

            int responseCode = connection.getResponseCode();
            switch (responseCode) {
                case HttpURLConnection.HTTP_PARTIAL:
                    // 服务器支持断点续传,继续下载
                    String contentRange = connection.getHeaderField("Content-Range");
                    long totalFileSize = Long.parseLong(contentRange.split("/")[1]);
                    System.out.println("继续下载:已完成 " + downloadedBytes + " / " + totalFileSize + " 字节");

                    try (InputStream inputStream = connection.getInputStream();
                         OutputStream outputStream = new FileOutputStream(zipFile, true)) {

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        long currentProgress = downloadedBytes;

                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                            currentProgress += bytesRead;

                            // 每下载1MB更新一次进度
                            if (currentProgress - downloadedBytes >= 1024 * 1024) {
                                updateProgress(currentProgress, progressFile);
                                downloadedBytes = currentProgress;
                                System.out.println("当前进度:" + currentProgress + " / " + totalFileSize + " 字节");
                            }
                        }

                        // 下载完成,删除进度文件
                        progressFile.delete();
                        System.out.println("文件下载完成!");
                    }
                    break;

                case HttpURLConnection.HTTP_OK:
                    // 服务器不支持断点续传,从头下载
                    System.out.println("服务器不支持断点续传,将从头开始下载");
                    try (InputStream inputStream = connection.getInputStream();
                         OutputStream outputStream = new FileOutputStream(zipFile)) {

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        long currentProgress = 0;

                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                            currentProgress += bytesRead;

                            if (currentProgress - downloadedBytes >= 1024 * 1024) {
                                updateProgress(currentProgress, progressFile);
                                downloadedBytes = currentProgress;
                                System.out.println("当前进度:" + currentProgress + " 字节");
                            }
                        }

                        progressFile.delete();
                        System.out.println("文件下载完成!");
                    }
                    break;

                case HttpURLConnection.HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
                    // 请求范围无效,已下载的字节超过了文件总大小,重新下载
                    System.out.println("已下载内容超过服务器文件大小,将重新下载");
                    zipFile.delete();
                    progressFile.delete();
                    main(args); // 重新启动下载
                    break;

                default:
                    System.err.println("下载失败,响应码:" + responseCode);
            }

            connection.disconnect();
        } catch (IOException e) {
            System.out.println("下载中断,已保存当前进度");
            e.printStackTrace();
            // 异常发生时保存进度
            try {
                updateProgress(downloadedBytes, progressFile);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    // 更新进度记录文件
    private static void updateProgress(long progress, File progressFile) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(progressFile))) {
            writer.write(String.valueOf(progress));
        }
    }
}

关键注意点

  • 服务器兼容性:不是所有服务器都支持Range请求,所以代码里一定要处理200 OK的情况,避免直接报错。
  • 进度可靠性:用单独的进度文件记录下载进度,同时校验本地文件大小和进度的一致性,防止用户手动修改文件导致的异常。
  • 文件完整性:下载完成后,可以对比本地文件大小和服务器返回的总大小,确保文件下载完整。如果服务器提供MD5/SHA哈希值,最好额外校验哈希值,保证文件没有损坏。
  • 异常场景处理:网络波动、应用崩溃等情况都要捕获,及时保存进度,避免之前的下载功亏一篑。

内容的提问来源于stack exchange,提问作者Latha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:32:41