We brush a following questions, sometimes will see some big companies will ask about the principle of breakpoint continuation, so today here from HTTP breakpoint continuation knowledge and Android how to achieve breakpoint continuation ideas to do a summary of the principle of Android breakpoint continuation.

Http breakpoints continue knowledge points

What is breakpoint continuation

Refers to the upload/download, the task (a file or a zip) artificially divided into several parts, each part using a thread to upload/download, if encounter network failure, can start from already upload/download part continue to upload/download unfinished part, and it is not necessary to upload/download from the very beginning. It saves time and speeds up.

How does Http support breakpoint continuation?

By default, Http 1.1 supports retrieving part of the Content of a file, which is mainly achieved by using two header parameters: Range and Content Range. The client sends a request and the server responds with a content-range.

Range

If the client wants to retrieve part of the file, it needs to specify the position of the start and end bytes in the Range parameter in the request header, which is generally in the following format:

Range:(unit=first byte pos)-[last byte pos]
Copy the code

Such as:

Range: bytes=0-499 Contents in the Range of 0-499 bytes Range: bytes=500-999 Contents in the Range of 500-999 bytes Range: bytes=-500 Contents in the last 500 bytes Range: Bytes =500- Indicates the contents from the 500th byte to the end of the file Range: bytes=0-0,-1 indicates the first and last bytes Range: bytes=500-600,601-999 Specifies several ranges at the same timeCopy the code

Content Range

After receiving a request from a client with a Range in it, the server adds the Content Range parameter to the header of the response, returning the acceptable Range of file bytes and the total size of the file. Its format is as follows:

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]

Copy the code

Such as:

Content-range: bytes 0-499/22400 // 0-499 indicates the Range of data currently sent, while 22400 indicates the total size of the file.Copy the code

The difference between the content of the response with and without breakpoint continuation

  • Do not use breakpoint continuation
HTTP / 1.1 200 OkCopy the code
  • Use breakpoint continuation
HTTP / 1.1 206 Partial ContentCopy the code

Handle the problem of requested resource changes

In real scenarios, the files in the server will change, so the request to initiate a continuation will definitely fail. In order to deal with the problem of the change of the server file resources, LAST-Modified and Etag are defined in RFC2616 to determine whether the continued file resources change.

Last-modified & if-modified-since (time when a file was Last Modified)

  • Last-modified: Http header parameter that records the Last modification time of the Http page. Last-modified is sent by the server to the client
  • If-modified-since: Indicates the Http header parameter that records the last modification time of the Http page. If-modified-since is sent by the client to the server
  • The validation process
    • Step 1: The client caches the page obtained from the server
    • Step 1: When the client accesses the same page, the client sends the last-modified message sent by the server to the server through if-modified-since
    • Step 2: The server uses if-modified-since sent by the client to determine whether the current cached page is the latest
      • If not, send the latest page to the client
      • If so, send 304 to tell the client that its locally cached pages are up to date

Etag & if-range (file unique flags)

  • Etag: A unique identifier for a file. This identifier can be a hash value or a version of the file
  • If-range: determines whether the entity has changed. If the entity has not changed, the server sends the missing part to the client, otherwise sends the whole entity. General format:
If-Range: Etag | HTTP-Date
Copy the code

If-range can use the value returned by Etag or last-Modified. Last-modified can be used as the value of the if-range field when there is no ETage but last-Modified

  • The validation process
    • Step 1: The client initiates a continuation request. The header contains Range and if-range parameters
    • Step 2: After the server receives the request from the client, compare the Etag between the client and the server
      • Equal: The requested file resources are not changed. The reply packet is 206
      • Unequal: The request file resource changes and the reply packet is 200

Check whether the server supports breakpoint continuation

  • HTTP / 1.1 206 Partial Content
  • Content-Range: bytes 10-222/7877
  • Etag: “1ec5-502264e2ae4c0”
  • Last-Modified: Wed, 03 Sep 2014 10:00:27 GMT

OkHttp breakpoint download

Breakpoint download

  • Step 1: Check whether the downloaded file exists. If yes, obtain the downloaded file size downloadLength. If no, the downloaded file length is 0
  • Get the total size of the file to be downloaded (content-Length of the HTTP response header).
  • Step 3: Compare the downloaded file size with the total file size (contentLength) to determine the length to be downloaded
  • Range: bytes = downloadLength – contentLength

Okhttp simple short breakpoint download code example

DownloadTask.java

/** * String Specifies the parameter that needs to be passed in to perform AsyncTask, which can be used in background tasks. * Integer If the current progress needs to be displayed on the page during background task execution, the specified generic type is used as the progress unit. * Integer When a task is completed and the result needs to be returned, the generic type specified here is used as the return value type. */ public class DownloadTask extends AsyncTask<String, Integer, Integer> { public static final int TYPE_SUCCESS = 0; public static final int TYPE_FAILED = 1; public static final int TYPE_PAUSED = 2; public static final int TYPE_CANCELED = 3; private DownloadListener listener; private boolean isCanceled =false;

    private boolean isPaused = false; private int lastProgress; public DownloadTask(DownloadListener listener) { this.listener = listener; } /** * All the code in this method will run in the child thread, where we should handle all the time-consuming tasks. * * @param params * @return
     */
    @Override
    protected Integer doInBackground(String... params) { InputStream is = null; RandomAccessFile savedFile = null; File file = null; long downloadLength = 0; String downloadUrl = params[0]; String downloadUrl = params[0]; // download fileName String fileName = downloadurl.substring (downloadurl.lastindexof ("/")); / / download file storage directory String directory = Environment. GetExternalStoragePublicDirectory (Environment. DIRECTORY_DOWNLOADS). GetPath ();  // create a file file = new file (directory + fileName);if(file.exists()) {// Get the size of the file if it exists downloadLength = file.length(); } long contentLength = getContentLength(downloadUrl);if (contentLength == 0) {
            return TYPE_FAILED;
        } else if(contentLength == downloadLength) {// If the downloaded bytes are equal to the total bytes of the file, the download is completereturnTYPE_SUCCESS; } OkHttpClient client = new OkHttpClient(); /** * HTTP requests have a Header that contains a Range property that defines the download area. This accepts a Range of values, such as Range:bytes=0-10000. In this way, we can divide a large file into several small parts according to certain rules, and then download them in batches. After downloading each small piece, we can merge it into the file. In this way, even if the download is interrupted, * can determine the starting point of the download by the byte length of the file when downloading again, and then restart the resumable process until the download process is completed. */ Request request = new Request.Builder() .addHeader("RANGE"."bytes=" + downloadLength + "-"+ contentLength) // The breakpoint to continue to use, indicating the download interval.url(downloadUrl).build(); try { Response response = client.newCall(request).execute();if(response ! = null) { is = response.body().byteStream(); savedFile = new RandomAccessFile(file,"rw"); savedFile.seek(downloadLength); Byte [] b = new byte[1024]; int total = 0; int len;while ((len = is.read(b)) != -1) {
                    if (isCanceled) {
                        return TYPE_CANCELED;
                    } else if (isPaused) {
                        return TYPE_PAUSED;
                    } else{ total += len; savedFile.write(b, 0, len); Int progress = (int) ((total + downloadLength) * 100 / contentLength); // Note: indoUI operations are not allowed in InBackground(); if you need to update the UI, such as reporting progress on the current task, you can call publishProgress(). publishProgress(progress); } } response.body().close();return TYPE_SUCCESS;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(is ! = null) { is.close(); }if(savedFile ! = null) { savedFile.close(); }if (isCanceled && file != null) {
                    file.delete();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        returnTYPE_FAILED; } /** * When publishProgress is called in background task (Progress...) The onProgressUpdate() method * is called immediately after the method, which carries the parameters passed in the background task. In this method, you can operate on the UI, using the values in the parameters * to update the interface accordingly. * * @param values */ @Override protected void onProgressUpdate(Integer... values) { int progress = values[0];if(progress > lastProgress) { listener.onProgress(progress); lastProgress = progress; }} /** * This method is called immediately after the background task has finished and is returned by a Return statement. The returned data is passed to this method as an argument *, and you can use the returned data to perform some UI operations. * * @param status */ @Override protected void onPostExecute(Integer status) { switch (status) {case TYPE_SUCCESS:
                listener.onSuccess();
                break;
            case TYPE_FAILED:
                listener.onFailed();
                break;
            case TYPE_PAUSED:
                listener.onPaused();
                break;
            case TYPE_CANCELED:
                listener.onCanceled();
                break;
            default:
                break;
        }
    }

    public void pauseDownload() {
        isPaused = true;
    }

    public void cancelDownload() {
        isCanceled = true; } /** ** get the full size of the downloaded content ** @param downloadUrl * @return
     */
    private long getContentLength(String downloadUrl) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(downloadUrl).build();
        try {
            Response response = client.newCall(request).execute();
            if(response ! = null && response.isSuccessful()) { long contentLength = response.body().contentLength(); response.body().close();return contentLength;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return0; }}Copy the code

DownloadListener.java

Public class DownloadListener {/** * notify the current download progress * @param progress */ void onProgress(int progress); /** */ void onSuccess(); /** */ void onFailed(); /** * void onPaused(); /** * Void onCanceled(); }Copy the code