preface

File downloads are used in many types of applications, such as music, documents, and even images (although images can be downloaded without awareness using some components). This paper introduces the Dio download method to complete the file download, and the contents involved are as follows:

  • Dio plug-indownloadMethod introduction;
  • usedownloadThe callback method to monitor the download progress;
  • useCancelTokenCancel the task being downloaded.
  • Delete downloaded files;
  • path_providerPlugin manages App file directory;
  • Some problems found in the debugging process of downloading files;

Dio download method

Dio download method is defined as follows:

Future<Response> download(
  String urlPath,
  savePath, {
  ProgressCallback? onReceiveProgress,
  Map<String.dynamic>? queryParameters,
  CancelToken? cancelToken,
  bool deleteOnError = true.String lengthHeader = Headers.contentLengthHeader,
  data,
  Options? options,
});
Copy the code
  • urlPath: of network resourcesurl;
  • savePath:dynamicType, which can be either a string that stores the path to the file after downloading, or a callback method that returns a string (Dio will do that)headersParameter is carried in the past, easy to build the file path for download returned content);
  • onReceiveProgress: File receiving progress, is onevoid Function(int count, int total)Callback function that allows the caller to monitor the download progress.
  • deleteOnError: whether to delete downloaded files when an error occurs. Default is true.
  • lengthHeader: The actual size of the source file (before compression). The default isheadercontent-length. If the file is compressed, and this value is not specified, the progress callbacktotalWill be- 1; If you use a customheaderThe size of the file is specified, sototalIt’s going to be customheaderThe corresponding file size.
  • The rest of the parameters are similar to ordinary requests and will not be described here.

Let’s continue our study with The Old Wang, hide the Dio and encapsulate our own download method in http_util.dart.

static Future download(
  String url,
  String savePath, {
  Map<String.dynamic> queryParams,
  CancelToken cancelToken,
  dynamic data,
  Options options,
  void Function(int.int) onReceiveProgress,
}) async {
  try {
    return await _dioInstance.download(
      url,
      savePath,
      queryParameters: queryParams,
      cancelToken: cancelToken,
      onReceiveProgress: onReceiveProgress,
    );
  } on DioError catch (e) {
    if (CancelToken.isCancel(e)) {
      EasyLoading.showInfo('Download cancelled! ');
    } else {
      if(e.response ! =null) {
        _handleErrorResponse(e.response);
      } else{ EasyLoading.showError(e.message); }}}on Exception catch(e) { EasyLoading.showError(e.toString()); }}Copy the code

Monitoring download Progress

Let’s create a new file download page, file_download.dart, to complete the file download example. There are several properties defined to give feedback on the file download process:

// File download address, here is the Google Chrome download address (Mac version)
String _downloadPath =
      'https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg';
// Download progress ratio, used to check whether the download is complete
double _downloadRatio = 0.0;
// Download progress percentage
String _downloadIndicator = '0.00%';
// The path where the file is stored
String _destPath;
// Cancel the downloaded token
CancelToken _token;
// Indicates whether you are currently downloading for a business judgment
bool _downloading = false;
Copy the code

Then we define a download method that updates the download progress during the download process if the total is not -1, otherwise an error is displayed (the actual debugging found, if it involves verification, the back-end will actually return to the web page after the download, which can also download the web page content down, but not the desired file).

void _downloadFile() {
  _token = CancelToken();
  _downloading = true;
  HttpUtil.download(_downloadPath, _destPath, cancelToken: _token,
      onReceiveProgress: (int received, int total) {
    if(total ! =- 1) {
      if(! _token.isCancelled) { setState(() { _downloadRatio = (received / total);if (_downloadRatio == 1) {
            _downloading = false;
          }
          _downloadIndicator =
              (_downloadRatio * 100).toStringAsFixed(2) + The '%'; }); }}else {
      _downloading = false;
      EasyLoading.showError('Unable to get file size, download failed! '); }}); }Copy the code

Because possible cancellations are involved, update the download status only if there is no cancellation, or if the cancellation is still in the process of receiving bytes, and you see that the download progress is still in progress.

Cancel the download

CancelToken CancelToken CancelToken CancelToken CancelToken CancelToken CancelToken CancelToken CancelToken In this case, we made a judgment that the download ratio should be less than 1 to cancel, because an exception will be thrown if the download is completed. Reset download percentage and display download percentage after cancellation.

Void _cancelDownload() {if (_downloadRatio < 1.0) {_token.cancel(); _downloading = false; setState(() { _downloadRatio = 0; _downloadIndicator = '0.00%'; }); }}Copy the code

Delete the downloaded file

For the App, there is no other entry to manage files, so in the actual process we need to provide a download entry for users to clean up the downloaded files. For the downloaded files, we need to have the download file management function for users to manage files. In this case, local storage is required. We will introduce local storage in the following chapters.

Before deleting a file, check whether the file exists. If no file exists, an exception may be thrown. The files are managed using methods in dart: IO.

void _deleteFile() {
  try {
    File downloadedFile = File(_destPath);
    if (downloadedFile.existsSync()) {
      downloadedFile.delete();
    } else {
      EasyLoading.showError('File does not exist'); }}catch(e) { EasyLoading.showError(e.toString()); }}Copy the code

Path_provider File directory management

There is no way to directly know the application file storage directory in the App, so you need to borrow the path_Provider plug-in to get the App file storage directory. Path_provider provides the following methods:

  • getTemporaryDirectory: Apply temporary directory (may be cleared)
  • getApplicationDocumentsDirectoryBy the system: the document directory (not clear, the main user data storage directory), recommend the use of external storage external.getexternalstoragedirectory for android.
  • getApplicationSupportDirectory: An application support directory where data that is not relevant to the user is stored.
  • getLibraryDirectory: Points to a directory where the app can persist data. Not supported on Android.
  • getExternalStorageDirectory: Obtains an external storage directory, which is not supported on iOS.
  • getExternalCacheDirectories: Obtain the external cache directory,, not supported on iOS.
  • getExternalStorageDirectories: Obtains the list of available external directories. The iOS platform is not supported.
  • getDownloadsDirectory: Download directory. It is used on the Web and does not support Android and iOS platforms.

After obtaining the Directory object through the path_provider, you can obtain the complete Directory path through the path attribute of the Directory. In this example, we get the file storage path in initialState, using a temporary directory.

void initState() {
  getTemporaryDirectory()
      .then((tempDir) => {_destPath = tempDir.path + 'googlechrome.dmg'});

  super.initState();
}
Copy the code

Some errors encountered during debugging

  • OS Error: Read-only file system: Required for AndroidREAD_EXTERNAL_STORAGEandWRITE_EXTERNAL_STORAGEPermissions. It also needs to be usedpath_providerObtain the file directory of the application, read and write files to the corresponding directory, and access the file directory.
  • onReceivedProgressIf in thetotal=-1Indicates that the file is compressed or requires session information to download (for example, authentication is enabled on the back end).
  • When deleting a file, check whether the file is in the download process. If the file is deleted during the download process, an exception is raised.
  • CancelTokenAn instance can only cancel a request once, so it needs to be rebuilt each time a request is madeCancelTokenObject, otherwise it cannot be cancelled again.

Run results and code

The running result is as shown in the figure below:

The code has been submitted to Gitee: Dio Network Request related code.

conclusion

As you can see from the example, Dio’s download method is easy to use and provides friendly download feedback. At the same time, with the aid of CancelToken can cancel or suspend the download (pause when setting deleteOnError = false, said not to delete files, and then recover in time from the downloaded offset, also can do in this way a breakpoint continuingly, specific ways to search or complete). The Dio Network requests series will be covered in detail in this article, and will be intercut with subsequent business applications. The next article will be an overall summary of the Dio series.