preface

This article uses the HTTP service of NodeJS to create the client, uses the Range request to realize the download function, and through this Demo extension in the business to realize the idea of breakpoint continuation and other functions.

Server-side implementation

In the server code, we use async functions to reduce callback nesting, so we need to convert asynchronous operation methods into promises. In the past, we use Util’s promisify to convert asynchronous methods one by one. This time we use the third party module MZ and directly introduce the converted replacement module.

Before using MZ, you need to install:

npm install mz

The server code is as follows:

File: server. Js
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52Copy the code
const http = require("http");
const path = require("path");
const url = require("url"); Const fs = require(const fs = require("mz/fs"); // Request handler asyncfunctionListener (req, res) {// Obtain the range request header in the format of range :bytes=0-5let range = req.headers["range"]; // Download file pathlet p = path.resovle(__dirname, url.parse(url, true).pathname); // The presence of a range request header will return the range request dataif(range) {// Get the start and end positions of the range requestlet[, start, end] = range.match(/(\d*)-(\d*)/); // Error handler try {let statObj = await fs.stat(p);
        } catch (e) {
            res.end("Not Found"); } // Total file byteslet total = statObj.size; Start = start? ParseInt(start) : 0; end = end ? ParseInt(end) : total - 1; // Response client res.statuscode = 206; res.setHeader("Accept-Ranges"."bytes");
        res.setHeader("Content-Range", `bytes ${start}-${end}/${total}`);
        fs.createReadStream(p, { start, end }).pipe(res);
    } else{// Return the entire file contents to the client fs.createreadstream (p).pipe(res); }} // createServer const server = http.createserver (listener); Server.listen (3000, () => {console.log()"server start 3000");
});
Copy the code

Range:bytes=0-5. This can be obtained by using the headers attribute of req. If the client sends a Range request, the request header is in Range:bytes=0-5. Originally, NodeJS uses lowercase letters to start with uppercase letters, so it should be lowercase.

If it is a Range request, the corresponding content is read through the readable stream and returned to the client; if it is not, the whole file is read through the readable stream and returned to the client. In the process of responding to the Range request, the response state needs to be set to 206 and the value of the response header accept-ranges needs to be set to bytes. You need to set the content-range value of the response header to byte 0-5/100, where 0 is the index at the beginning of the returned data, 5 is the index at the end (inclusive), and 100 is the total number of bytes in the file.

Error detection should be performed when resolving and concatenating the path to the downloaded file through the URL and path modules, and if the file does Not exist, the client is returned Not Found.

Curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl

Curl -v –header “Range:bytes=0-5” http://localhost:3000

Client-side implementation

Using the curl command to access the server, you can only request a fixed Range of data. You can download a Range of data every time, but you need to use the client logic to download and automatically maintain the Range.

For simplicity, our download client runs in the command line window and simulates the start download, pause and resume buttons in the actual project through instructions. The download starts when the S instruction is entered in the window, stops when the P instruction is entered, and resumes when the R instruction is entered.

File: client. Js
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68Copy the code
const http = require("http");
const fs = require("fs");
const path = require("path"); // Request configurationlet config = {
    host: "localhost",
    port: 3000,
    path: "/download.txt"
};

letstart = 0; // Request an initial valueletstep = 5; // Number of characters per requestlet pause = false; // Pause statelettotal; // Total file length // Create writable streamletws = fs.createWriteStream(path.resolve(__dirname, config.path.slice(1))); // Download the functionfunction download() {config.headers = {// set the range to step bytes per request."Range": `bytes=${start}-${start + step - 1}`; }; // Maintain the next start value start += step; Request (config, res => {// Get the total file lengthif(typeof total ! = ="number") {
            total = res.headers["content-ranges"].match(/\/(\d*)/)[1]; } // Read the returned datalet buffers = [];
        res.on("data", data => buffers.push(data));
        res.on("end", () => {// Merge data and write to fileletbuf = Buffer.concat(buffers); ws.write(buf); // Recursively make the next requestif(! pause && start < total) { download(); }}); }).end(); } // Monitor input process.stdin.on("data", data => {// get instructionlet ins = data.toString().match(/(\w*)\/r/)[1];
    switch (ins) {
        case "s":
        case "r":
            pause = false;
            download();
            break;
        case "p":
            pause = true;
            break; }});Copy the code

The file downloaded in the above code is configured with the Path property in config. Each download call recalculates the initial and end locations of the current Range request and sets the Range request header. The next request is implemented recursively.

During the execution, we need to start our server first, enter node client.js on the command line to start the client, and enter corresponding instructions in the command window to start, pause and resume the download operations.

conclusion

Believe now know what is the scope of the request, the scope of the request the client and the server needs to be done, in fact, namely the use of the corresponding request and reply headers, it’s important to note that the scope of the request the response status code of 206, the demand in some of the upload, download resources website is also common, its purpose is to let us realize the breakpoint continuingly, Not a complete upload or download of the resource file, the next time to do the same operation need to come back, you can continue to the last position, range request is also widely used in video websites, while watching the request, not a load of the entire video resources, save traffic, save time.

The original source: https://www.pandashen.com