You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter技术实现:HTTP请求如何获取上传/下载进度

Absolutely! You can absolutely stream large files without loading them entirely into memory and track upload progress using Dart/Flutter's built-in APIs. Let's break down how to fix your broken pipe issue and add progress tracking step by step.

The Problem with Your Current Code

Your existing code uses request.addStream(stream) which hands off the entire file stream to the HttpClient in one go. This means you can't track how much data has been sent, and it can lead to broken pipe errors if the stream is too large or the connection can't keep up with the data flow.

Solution: Manual Chunk Processing for Progress Tracking

Instead of using addStream, we'll process the file stream in chunks, update our progress after each chunk is sent, and write each chunk to the request individually. This keeps memory usage low (since we only load one chunk at a time) and gives us granular progress updates.

Here's a revised version of your upload function with progress tracking:

typedef UploadProgressCallback = void Function(int sentBytes, int totalBytes);

Future<String> _uploadFile(File assetFile, UploadProgressCallback onProgress) async {
  final url = '<removed>';
  final totalLength = assetFile.lengthSync();
  int sentBytes = 0;

  final client = HttpClient()
    ..connectionTimeout = const Duration(seconds: 30); // Prevent timeout issues

  try {
    final request = await client.postUrl(Uri.parse(url));
    request.headers.add(HttpHeaders.contentTypeHeader, "application/octet-stream");
    request.contentLength = totalLength;

    // Process the file stream chunk by chunk
    await for (final chunk in assetFile.openRead()) {
      await request.add(chunk);
      sentBytes += chunk.length;
      // Trigger progress update
      onProgress(sentBytes, totalLength);

      // Optional: Add a small delay if you're still getting broken pipe errors
      // This helps prevent overwhelming the connection
      // await Future.delayed(const Duration(milliseconds: 10));
    }

    final response = await request.close();
    if (response.statusCode == HttpStatus.ok) {
      return await response.transform(utf8.decoder).join();
    } else {
      throw Exception('Upload failed with status code: ${response.statusCode}');
    }
  } finally {
    client.close(); // Ensure we clean up the client
  }
}

How to Use This Function

Call it with your file and a progress callback to update your UI:

_uploadFile(yourFile, (sent, total) {
  final progressPercentage = (sent / total) * 100;
  print('Upload progress: ${progressPercentage.toStringAsFixed(1)}%');
  // Update your progress indicator here (e.g., a LinearProgressIndicator)
});

Fixing the Broken Pipe Issue

Broken pipe errors usually happen when the connection is interrupted mid-transfer, or the server can't handle the data flow speed. Here are some targeted fixes:

  • Add a connection timeout: As shown in the code, setting connectionTimeout ensures the client doesn't hang indefinitely if the connection drops.
  • Throttle chunk sending: The optional delay in the chunk loop can prevent overwhelming the server or network buffer.
  • Verify server configuration: Make sure your server accepts large application/octet-stream requests and has sufficient timeout settings.
  • Handle retries: For unstable networks, you might want to add retry logic for failed chunks (stick to idempotent requests only to avoid duplicate uploads).

Alternative: Using Stream Transformer

If you prefer to use addStream, you can wrap the file stream in a StreamTransformer to track progress as chunks pass through:

Future<String> _uploadFileWithTransformer(File assetFile, UploadProgressCallback onProgress) async {
  final url = '<removed>';
  final totalLength = assetFile.lengthSync();
  int sentBytes = 0;

  final client = HttpClient();
  try {
    final request = await client.postUrl(Uri.parse(url));
    request.headers.add(HttpHeaders.contentTypeHeader, "application/octet-stream");
    request.contentLength = totalLength;

    final progressStream = assetFile.openRead().transform(
      StreamTransformer.fromHandlers(
        handleData: (chunk, sink) {
          sentBytes += chunk.length;
          onProgress(sentBytes, totalLength);
          sink.add(chunk);
        },
      ),
    );

    await request.addStream(progressStream);
    final response = await request.close();
    return await response.transform(utf8.decoder).join();
  } finally {
    client.close();
  }
}

This works too, but the chunk-by-chunk await for approach gives you more control over the transmission flow, which can help mitigate broken pipe issues.

Key Notes

  • assetFile.lengthSync() is safe even for large files—it only reads file metadata, not the entire content.
  • Always close the HttpClient in a finally block to avoid resource leaks.
  • Double-check that your server correctly handles application/octet-stream requests (not expecting multipart data).

内容的提问来源于stack exchange,提问作者Jeff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:30:56