OkHttp elegantly encapsulates upload and download decryption of HttpUtils

I used to be unruly in the code, and now I work day and night in blog posts, just to share the results with you today. If you find this article useful, remember to follow me and I will bring you more.

If you haven't read the first article, welcome to move: OkHttp elegantly encapsulates the spirit of HttpUtils

introduce

HttpUtils is a recently open-sourced framework for lightweight encapsulation of OkHttp. Its original asynchronous preprocessor , unique tags , flexible upload and download progress monitoring and process control functions can easily solve many headaches. Also strive for purity and elegance.

  • Chain call, one point to the end
  • BaseURL, URL placeholder, JSON automatic packaging and parsing
  • Synchronous interceptors, asynchronous preprocessors, callback executors
  • File upload and download (process control, progress monitoring)
  • TCP connection pool, Http2

Project address Gitee: https://gitee.com/ejlchina-zhxu/httputils GitHub: https://github.com/ejlchina/httputils

Installation tutorial

Maven

<dependency>
     <groupId>com.ejlchina</groupId>
     <artifactId>httputils</artifactId>
     <version>2.2.0</version>
</dependency>

Gradle

compile 'com.ejlchina:httputils:2.2.0'

  Before the official start, we still assume that the following examples are httpall set at build time ( see the previous section for details ):BaseUrlHTTP

HTTP http = HTTP.builder()
        .baseUrl("http://api.demo.com")
        .build();

OK, everything is ready, let's continue with the previous article .

8 File Download

  HttpUtils does not exclude file downloads from regular requests, but uses the same set of APIs. Its elegant design makes downloading and regular requests merge without any sense of incongruity. A simple example:

http.sync("/download/test.zip")
        .get()                           // 使用 GET 方法(其它方法也可以,看服务器支持)
        .getBody()                       // 得到报文体
        .toFolder("D:/download")         // 下载到指定目录,文件名将根据下载信息自动生成
        .start();                        // 启动下载

  Or use asynchronous request method:

http.async("/download/test.zip")
        .setOnResponse((HttpResult result) -> {
            //  下载到指定路径
            result.getBody().toFile("D:/download/test.zip").start();
        })
        .get();

  Here to explain: syncthe asyncdifference between and is that the process of connecting to the server and getting a response is synchronous and asynchronous (the time-consuming of this process is very small in large file downloads), while startthe download process initiated by the method is asynchronous.

8.1 Download progress monitoring

  Just go to the code directly, I believe you will understand at a glance:

http.sync("/download/test.zip")
        .get()
        .getBody()
        .setStepBytes(1024)   // 设置每接收 1024 个字节执行一次进度回调(不设置默认为 8192)  
 //     .setStepRate(0.01)    // 设置每接收 1% 执行一次进度回调(不设置以 StepBytes 为准)  
        .setOnProcess((Process process) -> {           // 下载进度回调
            long doneBytes = process.getDoneBytes();   // 已下载字节数
            long totalBytes = process.getTotalBytes(); // 总共的字节数
            double rate = process.getRate();           // 已下载的比例
            boolean isDone = process.isDone();         // 是否下载完成
        })
        .toFolder("D:/download/")        // 指定下载的目录,文件名将根据下载信息自动生成
 //     .toFile("D:/download/test.zip")  // 指定下载的路径,若文件已存在则覆盖
        .setOnSuccess((File file) -> {   // 下载成功回调
            
        })
        .start();

  It is worth mentioning that: since HttpUtils does not do downloading very special, the progress callback set here is not only used for downloading files, even for regular requests in response to JSON, as long as the progress callback is set, it will also tell you the message The progress of the reception (the message that the server responds in advance has a Content-Lengthheader), for example:

List<User> users = http.sync("/users")
        .get()
        .getBody()
        .setStepBytes(2)
        .setOnProcess((Process process) -> {
            System.out.println(process.getRate());
        })
        .toList(User.class);

8.2 Download process control

  Too simple: or go directly to the code:

Ctrl ctrl = http.sync("/download/test.zip")
        .get()
        .getBody()
        .setOnProcess((Process process) -> {
            System.out.println(process.getRate());
        })
        .toFolder("D:/download/")
        .start();   // 该方法返回一个下载过程控制器
 
ctrl.status();      // 下载状态
ctrl.pause();       // 暂停下载
ctrl.resume();      // 恢复下载
ctrl.cancel();      // 取消下载(同时会删除文件,不可恢复)

  Whether it is a synchronous or asynchronous download request, the above control can be done:

http.async("/download/test.zip")
        .setOnResponse((HttpResult result) -> {
            // 拿到下载控制器
            Ctrl ctrl = result.getBody().toFolder("D:/download/").start();
        })
        .get();

8.3 Implementing a breakpoint resume

  HttpUtils does not do a higher level of encapsulation for resuming the upload, because this is what the app should do. It is designed to simplify the handling of various network problems while striving for purity. As you can see in the following example, HttpUtils gets a breakpoint through a failure callback, which makes complex problems simple:

http.sync("/download/test.zip")
        .get()
        .getBody()
        .toFolder("D:/download/")
        .setOnFailure((Failure failure) -> {         // 下载失败回调,以便接收诸如网络错误等失败信息
            IOException e = failure.getException();  // 具体的异常信息
            long doneBytes = failure.getDoneBytes(); // 已下载的字节数(断点),需要保存,用于断点续传
            File file = failure.getFile();           // 下载生成的文件,需要保存 ,用于断点续传(只保存路径也可以)
        })
        .start();

  Then implement breakpoint resume:

long doneBytes = ...    // 拿到保存的断点
File file =  ...        // 待续传的文件

http.sync("/download/test.zip")
        .setRange(doneBytes)                         // 设置断点(已下载的字节数)
        .get()
        .getBody()
        .toFile(file)                                // 下载到同一个文件里
        .setAppended()                               // 开启文件追加模式
        .setOnSuccess((File file) -> {

        })
        .setOnFailure((Failure failure) -> {
        
        })
        .start();

8.4 Implementing chunked downloads

  When the file is very large, sometimes we will consider downloading in chunks, which is the same as the idea of ​​resuming the upload. Example code:

static String url = "http://api.demo.com/download/test.zip"

public static void main(String[] args) {
    long totalSize = HttpUtils.sync(url).get().getBody()
            .close()             // 因为这次请求只是为了获得文件大小,不消费报文体,所以直接关闭
            .getContentLength(); // 获得待下载文件的大小(由于未消费报文体,所以该请求不会消耗下载报文体的时间和网络流量)
    download(totalSize, 0);      // 从第 0 块开始下载
    sleep(50000);                // 等待下载完成(不然本例的主线程就结束啦)
}

static void download(long totalSize, int index) {
    long size = 3 * 1024 * 1024;                 // 每块下载 3M  
    long start = index * size;
    long end = Math.min(start + size, totalSize);
    HttpUtils.sync(url)
            .setRange(start, end)                // 设置本次下载的范围
            .get().getBody()
            .toFile("D:/download/test.zip")      // 下载到同一个文件里
            .setAppended()                       // 开启文件追加模式
            .setOnSuccess((File file) -> {
                if (end < totalSize) {           // 若未下载完,则继续下载下一块
                    download(totalSize, index + 1); 
                } else {
                    System.out.println("下载完成");
                }
            })
            .start();
}

The class is used in this example. For HttpUtilsits detailed introduction, please refer to the previous article: A preliminary study of OkHttp's elegant encapsulation of HttpUtils .

9 File upload

  An example of a simple file upload:

http.sync("/upload")
        .addFileParam("test", "D:/download/test.zip")
        .post()     // 上传发法一般使用 POST 或 PUT,看服务器支持

  Asynchronous upload is exactly the same:

http.async("/upload")
        .addFileParam("test", "D:/download/test.zip")
        .post()

9.1 Upload progress monitoring

   The upload progress monitoring of HttpUtils monitors the sending progress of all request message bodies. Example code:

http.sync("/upload")
        .addBodyParam("name", "Jack")
        .addBodyParam("age", 20)
        .addFileParam("avatar", "D:/image/avatar.jpg")
        .setStepBytes(1024)   // 设置每发送 1024 个字节执行一次进度回调(不设置默认为 8192)  
 //     .setStepRate(0.01)    // 设置每发送 1% 执行一次进度回调(不设置以 StepBytes 为准)  
        .setOnProcess((Process process) -> {           // 上传进度回调
            long doneBytes = process.getDoneBytes();   // 已发送字节数
            long totalBytes = process.getTotalBytes(); // 总共的字节数
            double rate = process.getRate();           // 已发送的比例
            boolean isDone = process.isDone();         // 是否发送完成
        })
        .post()

  what! How does it feel to be the same as the download progress callback? That's right! HttpUtils still uses the same set of APIs to handle upload and download progress callbacks, the only difference is that upload get/postuses these APIs before the method, and download is getBodyused after the method. It's easy to understand: get/postbefore, it was the time to prepare to send the request, which means uploading, and getBodythen, it was the time for the message response, of course, the download.

9.2 Upload process control

  The process control of uploading files is very simple. Like regular requests, only asynchronously initiated uploads can be canceled:

HttpCall call = http.async("/upload")
        .addFileParam("test", "D:/download/test.zip")
        .setOnProcess((Process process) -> {
            System.out.println(process.getRate());
        })
        .post()

call.cancel();  // 取消上传

  There is no pause and resume function for uploading, no one should have this demand, right?

Previous article: A Preliminary Exploration of OkHttp’s Elegant Encapsulation of HttpUtils in the Sea and Snow Mountain Next article: The Magical Change of the Callback Thread of OkHttp’s Elegant Encapsulation of HttpUtils (stay tuned)


I used to be unruly in the code, and now I work day and night in blog posts, just to share the results with you today. If you find this article useful, remember to follow me and I will bring you more.

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324079689&siteId=291194637