okhttp断点续传

使用 OkHttp 实现断点续传,关键是通过设置 Range 请求头向服务器请求文件的部分内容。下面是详细的实现步骤和示例代码。

步骤

  1. 获取文件的总大小:在开始下载前,通常需要知道文件的总大小,可以通过发送一个初始请求来获取。
  2. 记录已下载字节数:记录已下载的字节数,确保在下载中断后能够继续。
  3. 分块下载 :每次发送一个 Range 请求,告诉服务器从哪个字节位置开始传输文件,并获取相应的字节块。
  4. 处理中断续传:如果下载过程中中断,可以通过记录的已下载字节数,从上次停止的位置继续下载。

代码实现

1. 添加 OkHttp 库

build.gradle 中添加 OkHttp 的依赖:

java 复制代码
dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.9.0")
}
2. 定义下载函数

使用 Range 请求头来获取文件的部分内容。

java 复制代码
import okhttp3.*;
import java.io.*;
import java.util.concurrent.TimeUnit;

public class FileDownloader {
    private static final String FILE_URL = "http://example.com/largefile.zip";  // 文件 URL
    private static final String FILE_PATH = "your/local/path/largefile.zip";    // 保存路径
    private static final long CHUNK_SIZE = 1024 * 1024;  // 每次下载 1MB

    private OkHttpClient client;
    private long downloadedBytes = 0;  // 已下载字节数

    public FileDownloader() {
        client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();
    }

    // 获取已下载的字节数
    private long getDownloadedBytes() {
        File file = new File(FILE_PATH);
        if (file.exists()) {
            return file.length();
        }
        return 0;
    }

    // 保存下载的数据到本地文件
    private void saveToFile(byte[] data) {
        try (FileOutputStream fos = new FileOutputStream(FILE_PATH, true)) {  // 以追加模式写入文件
            fos.write(data);
            fos.flush();
            downloadedBytes += data.length;  // 更新已下载字节数
            System.out.println("Downloaded " + downloadedBytes + " bytes.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 下载文件块
    private void downloadFileChunk(long startByte, long endByte) {
        Request request = new Request.Builder()
                .url(FILE_URL)
                .header("Range", "bytes=" + startByte + "-" + endByte)  // 设定下载范围
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
                System.out.println("Download failed");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    byte[] data = response.body().bytes();
                    saveToFile(data);  // 保存文件
                    if (downloadedBytes < response.body().contentLength()) {
                        // 继续下载下一个部分
                        long nextStartByte = downloadedBytes;
                        long nextEndByte = Math.min(nextStartByte + CHUNK_SIZE - 1, response.body().contentLength());
                        downloadFileChunk(nextStartByte, nextEndByte);  // 递归下载下一个部分
                    } else {
                        System.out.println("Download completed");
                    }
                } else {
                    System.out.println("Server response failed: " + response.code());
                }
            }
        });
    }

    // 启动下载任务
    public void startDownload() {
        downloadedBytes = getDownloadedBytes();  // 获取当前已下载的字节数
        long totalFileSize = getFileSize();  // 获取文件的总大小
        long startByte = downloadedBytes;
        long endByte = Math.min(startByte + CHUNK_SIZE - 1, totalFileSize);

        downloadFileChunk(startByte, endByte);  // 开始下载文件
    }

    // 获取文件总大小(可以通过 HEAD 请求或事先知道文件的大小)
    private long getFileSize() {
        Request request = new Request.Builder()
                .url(FILE_URL)
                .head()  // HEAD 请求
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                return response.body().contentLength();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return 0;
    }
}

解释

  1. 获取已下载的字节数

    • 通过 getDownloadedBytes() 方法来检查本地文件是否已经部分下载。如果文件已存在,返回已下载的字节数。
  2. 分块下载

    • 每次下载 1MB(CHUNK_SIZE 设置为 1MB),通过 Range 请求头向服务器请求文件的指定字节范围(例如 Range: bytes=0-1048575 请求从 0 到 1MB 的文件)。
  3. 保存文件并更新进度

    • 下载的文件块会通过 saveToFile() 方法保存到本地。如果下载的文件还没有完成,则继续请求下一个块。下载完成时,会打印 Download completed
  4. 获取文件总大小

    • 使用 HEAD 请求获取文件的大小,确保可以计算分块的范围。如果你已知文件的总大小,也可以省略这一步。
  5. 递归下载

    • 下载完成一个块后,递归调用 downloadFileChunk() 方法下载下一个块,直到文件完全下载。
相关推荐
东东__net3 天前
27_promise
okhttp
阿湯哥3 天前
SSE SseEmitter.completeWithError(e) 触发的处理逻辑
okhttp
刘大猫.8 天前
vue java 实现大地图切片上传
java·vue.js·上传·大文件上传·切片上传·断点续传·大地图上传
每次的天空8 天前
Android第六次面试总结(okhttp篇)
android·okhttp
胡图蛋.9 天前
AJAX的理解和原理还有概念
okhttp
Blue.ztl10 天前
Ajax与Axios,以及Apifox的入门使用
前端·ajax·okhttp
字节王德发12 天前
为什么Django能有效防御CSRF攻击?
okhttp·django·csrf
逆风飞翔的小叔16 天前
【微服务】java中http调用组件深入实战详解
okhttp·httpclient·resttemplate·http组件详解·httpclient详解
Rverdoser19 天前
封装AJAX(带详细注释)
okhttp
老梁学Android20 天前
okhttp源码解析
android·网络·okhttp