breakPointDownload method

void breakPointDownload({
  1. required String savePath,
  2. ProgressCallback? onReceiveProgress,
  3. Success? success,
  4. Failure? failure,
  5. Completed? completed,
  6. dynamic cancelCallback()?,
})

断点下载

Implementation

void breakPointDownload({
  required String savePath,
  adapter.ProgressCallback? onReceiveProgress,
  Success? success,
  Failure? failure,
  Completed? completed,
  Function()? cancelCallback,
}) async {
  if (!(await _checkNetWork())) {
    return;
  }

  int downloadStart = 0;
  File file = File(savePath);

  try {
    final url = _buildFinalUrl();

    if (file.existsSync()) {
      downloadStart = file.lengthSync();
    }

    // 添加 Range 头部
    final headers = _buildHeaders();
    headers["Range"] = "bytes=$downloadStart-";

    // 准备请求体
    dynamic requestBody = _rawBody;
    if (_rawBody == null && _bodyParams.isNotEmpty) {
      if (_bodyType == RequestBodyType.formData) {
        requestBody = _bodyParams;
      } else if (_bodyType == RequestBodyType.json) {
        requestBody = _bodyParams;
      }
    }

    // 构建 AdapterRequest,外部通常需要将responseType设置为 stream 响应类型
    final adapterRequest = adapter_models.AdapterRequest(
      baseUrl: _rxNet.baseUrl,
      path: url,
      method: _HttpMethod,
      queryParams: _queryParams,
      headers: headers,
      rawBody: requestBody,
      contentType: _contentType,
      responseType: _convertResponseType(),
      sendTimeout: _sendTimeout,
      receiveTimeout: _receiveTimeout,
      cancelToken: _cancelToken, // Use the actual cancel token
    );

    // 使用适配器发送请求
    final adapter = _rxNet.getAdapter();
    if (adapter == null) {
      throw NetworkException("NetworkAdapter is not initialized", null);
    }

    final response = await adapter.request(adapterRequest);
    onResponse?.call(response);

    // 处理流式响应
    if (response.data is Stream<List<int>>) {
      RandomAccessFile raf = file.openSync(mode: FileMode.append);
      Stream<Uint8List> stream = (response.data as Stream<List<int>>).map((data) => Uint8List.fromList(data));

      // 获取总大小
      final contentRange = response.getHeader('content-range');
      final total = int.tryParse(contentRange?.split('/').last ?? "0") ?? 0;

      final subscription = stream.listen((data) {
        raf.writeFromSync(data);
        downloadStart = downloadStart + data.length;
        if (total > 0 && total < downloadStart) {
          onReceiveProgress?.call(total, total);
          return;
        }
        onReceiveProgress?.call(downloadStart, total);
      }, onDone: () async {
        success?.call(file, SourcesType.net);
        await raf.close();
      }, onError: (e) async {
        failure?.call(e);
        await raf.close();
      }, cancelOnError: true);

      // 处理取消(简化版本,因为我们现在使用字符串标识)
      // 实际的取消逻辑由适配器处理
    } else {
      // 如果不是流式响应,检查是否已完成
      final contentRange = response.getHeader('content-range');
      final total = int.tryParse(contentRange?.split('/').last ?? "0") ?? 0;
      if (total <= downloadStart) {
        onReceiveProgress?.call(total, total);
        success?.call(file, SourcesType.net);
      }
    }
  } on AdapterException catch (error) {
    if (error.type == AdapterExceptionType.cancel) {
      cancelCallback?.call();
    } else {
      failure?.call(error);
    }
  } catch (e) {
    failure?.call(e);
  }

  completed?.call();
}