download method

  1. @override
Future<Response> download(
  1. String urlPath,
  2. dynamic savePath, {
  3. ProgressCallback? onReceiveProgress,
  4. Map<String, dynamic>? queryParameters,
  5. CancelToken? cancelToken,
  6. bool deleteOnError = true,
  7. FileAccessMode fileAccessMode = FileAccessMode.write,
  8. String lengthHeader = Headers.contentLengthHeader,
  9. Object? data,
  10. Options? options,
})
override

Download the file and save it in local. The default http method is "GET", you can custom it by Options.method.

urlPath is the file url.

The file will be saved to the path specified by savePath. The following two types are accepted:

  1. String: A path, eg "xs.jpg"
  2. FutureOr<String> Function(Headers headers), for example:
    await dio.download(
      url,
      (Headers headers) {
        // Extra info: redirect counts
        print(headers.value('redirects'));
        // Extra info: real uri
        print(headers.value('uri'));
        // ...
        return (await getTemporaryDirectory()).path + 'file_name';
      },
    );
    

On Web, browsers do not allow writing to arbitrary local file paths. In that environment, savePath is used as the suggested filename for the browser download. The browser decides the actual saved location, and the returned Response only means the response was fetched and the download was triggered. Web downloads load the whole response into memory before triggering the browser download, remain subject to CORS, do not support FileAccessMode.append, and ignore deleteOnError because there is no local file managed by Dio. The lengthHeader override is also not used on Web; progress totals come from the browser response progress event.

onReceiveProgress is the callback to listen downloading progress. Please refer to ProgressCallback.

deleteOnError whether delete the file when error occurs. The default value is true.

fileAccessMode {@macro dio.options.FileAccessMode}

lengthHeader : The real size of original file (not compressed). When file is compressed:

  1. If this value is 'content-length', the total argument of onReceiveProgress will be -1.
  2. If this value is not 'content-length', maybe a custom header indicates the original file size, the total argument of onReceiveProgress will be this header value.

You can also disable the compression by specifying the 'accept-encoding' header value as '*' to assure the value of total argument of onReceiveProgress is not -1. For example:

await dio.download(
  url,
  (await getTemporaryDirectory()).path + 'flutter.svg',
  options: Options(
    headers: {HttpHeaders.acceptEncodingHeader: '*'}, // Disable gzip
  ),
  onReceiveProgress: (received, total) {
    if (total <= 0) return;
    print('percentage: ${(received / total * 100).toStringAsFixed(0)}%');
  },
);

Implementation

@override
Future<Response> download(
  String urlPath,
  dynamic savePath, {
  ProgressCallback? onReceiveProgress,
  Map<String, dynamic>? queryParameters,
  CancelToken? cancelToken,
  bool deleteOnError = true,
  FileAccessMode fileAccessMode = FileAccessMode.write,
  String lengthHeader = Headers.contentLengthHeader,
  Object? data,
  Options? options,
}) async {
  if (fileAccessMode == FileAccessMode.append) {
    throw UnsupportedError(
      'The append mode is not available when downloading files on Web.',
    );
  }
  if (savePath is! String &&
      savePath is! FutureOr<String> Function(Headers)) {
    throw ArgumentError.value(
      savePath.runtimeType,
      'savePath',
      'The type must be `String` or `FutureOr<String> Function(Headers)`.',
    );
  }

  options ??= Options(method: 'GET');
  // Do not modify previous options.
  options = options.copyWith(responseType: ResponseType.bytes);

  final response = await request<List<int>>(
    urlPath,
    data: data,
    options: options,
    queryParameters: queryParameters,
    cancelToken: cancelToken,
    onReceiveProgress: onReceiveProgress,
  );
  final filename = await _resolveFilename(savePath, response);
  final cancelError = cancelToken?.cancelError;
  if (cancelError != null) {
    throw cancelError;
  }
  final responseBytes = response.data;
  final bytes = responseBytes == null
      ? Uint8List(0)
      : responseBytes is Uint8List
          ? responseBytes
          : Uint8List.fromList(responseBytes);
  try {
    download_trigger.triggerBrowserDownload(
      bytes: bytes,
      filename: filename,
      contentType: response.headers.value(Headers.contentTypeHeader),
    );
  } catch (e, s) {
    if (e is DioException) {
      rethrow;
    }
    throw DioException(
      requestOptions: response.requestOptions,
      response: response,
      error: e,
      stackTrace: s,
    );
  }
  return response;
}