The paper

It is common to download files in apps. For example, audio and video software cache resources offline, or Android phones download Apk installation packages to upgrade and so on. After reviewing the Open source libraries for React Native downloads, there is no complete solution for downloading, pausing, continuing the download, and recording the download status. After some research, it is technically possible to do this via RN.

Core Technical Solution

There are two key technical points to implement:

  1. How to manage and maintain download status locally to display and manage current download tasks in the download list.
  2. Download, pause, exit APP and open again how to continue downloading.

Async Storage

[open source] github.com/react-nativ…

AsyncStorage is a simple, asynchronous and persistent key-value storage system. From the perspective of the front end, it is equivalent to LocalStorage. The data saved in AsyncStorage will not be lost due to exit or upgrade operations. Therefore, it can be used as a technical solution for persistent storage.

RN-Fetch-Blob

[open source] github.com/joltup/rn-f…

Rn-fetch -Blob supports file upload and download, and provides a series of apis for file operations. More importantly, rn-fetch -Blob can support streaming download for large files, and temporarily store the downloaded part locally. This feature is the key to continue downloading after interruption.

Request Header Range parameter

【 reference 】developer.mozilla.org/zh-CN/docs/…

The Range parameter can be set in the Header of an HTTP Request. For an HTTP Request, the Range parameter can be set: Bytes =${preSize}-, the result of the request is a 206 Partial Content status code.

Implementation approach

Look at the above several core technology solutions, we realize the idea has been very clear. When downloading again after downloading interruption, you only need to obtain the size of the downloaded temporary file, and then set the downloaded Range:bytes=${preSize}-After the download is complete, the temporary files are appended to the target file so that the final result can be spliced together into a complete file.

The downloaded task list, status, progress and other information can be stored persistently through AsyncStorage and displayed at the UI level. Open the APP again and if the status is unfinished, you can continue downloading in wifi environment.

Key code Examples

Pretreatment stage

The target file is pre-created before downloading, and the existing temporary file is written to the target file, so that the size of the new file is used as the starting point for the local request Range parameter, of course, the first start is preSize is 0.

// Const filePath = 'XXXXX'; // Temporary file const tmpFilePath = 'xxxxx. TMP '// precreate file if (! await fs.exists(filePath)) { await fs.createFile(filePath, '', 'utf8'); } // Merge temporary files into formal files if (await fs.exists(tmpFilePath)) {let tmpFile = await fs.stat(tmpFilePath); tmpFile && await fs.appendFile(filePath, tmpFilePath, 'uri'); Unlink (tmpFilePath)} let fileStat = await fs.stat(filePath); const preSize = fileStat.size;Copy the code

Download the phase

You can set the fileCache and Header parameters of RNFetchBlob during the download phase

Rnfetchblob. config({path: tmpFilePath, fileCache: true, // stream download}). Fetch ('GET', url, {Range: Progress ((received, total) => {console.log('progress', A received/total)}). Then ((result) = > {} / / download success). The catch ((error) = > {} / / download failure)Copy the code

Post-download processing

After downloading, you need to append the file to the target file, and it is best to verify the size of the final file, and remove temporary files

await fs.appendFile(filePath, res.path(), 'uri'); fs.unlink(tmpFilePath); fileStat = await fs.stat(filePath); // Verify file size if(filestat.size! == FILE_SIZE){// Delete the error file fs.unlink(filestat.path); }else{// Download successfully}Copy the code

Possible problems

Rn-fetch -Blob stores the incorrect request information to a file

If the address range of the resource file is not a resource file but some 4xx or 5XX exception status code, it will simply write the error message directly to the file without error.

Request scope out of bounds problem

If the Requested file size is 1024 bytes, the valid Range is: bytes=0-1023. If there is a problem with the Requested Range Not Satisfiable, the interface will return the status code (416 Requested Range Not Satisfiable). Rn-fetch -Blob also writes the status code to the file without error.

Downloading is a problem of high memory usage

Rn-fetch -Blob adopts streaming download, but files occupy a large amount of memory resources during the download process. Oversized files may lead to exceptions due to high memory usage. Multiple download tasks can be split according to the specified size, and different Range headers can be specified for each task, and finally splintered together. This avoids the memory problem of a single large file download.

The IOS sandbox path is faulty

There is a very serious problem here. After the update of the IOS APP, we found that all downloaded files could not be found. After the original file is downloaded, AsyncStorage stores the complete absolute path of the file. However, IOS will change the new sandbox root directory after each update, so the original saved file path does not exist. The solution is to re-sandbox the root directory every time.

The resources

  • HTTP Header Range
  • Use rn-fetch-blob to download
  • Description of iOS sandbox path changes