Dart - HTTPClient 下载文件到字符串

问题描述

在我目前正在开发的 Flutter/Dart 应用程序中,需要从我的服务器下载大文件。但是,我需要做的不是将文件存储在本地存储中,而是解析其内容一次性使用它。我认为实现此目的的最佳方法是实现我自己的 StreamConsumer 并覆盖相关方法。这是我到目前为止所做的

import 'dart:io';
import 'dart:async';

class Accumulator extends StreamConsumer<List<int>>
{
 String text = '';

 @override
  Future<void> addStream(Stream<List<int>> s) async
  {
   print('Adding'); 
   //print(s.length); 
   return; 
  }

 @override 
 Future<dynamic> close() async
 {
  print('closed'); 
  return Future.value(text);
 }
}

Future<String> fileFetch() async
{
 String url = 'https://file.io/bse4moAYc7gW'; 
 final HttpClientRequest request = await HttpClient().getUrl(Uri.parse(url));
 final HttpClientResponse response = await request.close();
 return await response.pipe(Accumulator());
}

Future<void> simpleFetch() async
{
 String url = 'https://file.io/bse4moAYc7gW'; 
 final HttpClientRequest request = await HttpClient().getUrl(Uri.parse(url));
 final HttpClientResponse response = await request.close();
 await response.pipe(File('sample.txt').openWrite());
 print('Simple done!!');  
}

void main() async 
{
 print('Starting'); 
 await simpleFetch(); 
 String text = await fileFetch();
 print('Finished! $text');
}

当我在 VSCode 中运行它时,这是我得到的输出

Starting
Simple done!! //the contents of the file at https://file.io/bse4moAYc7gW are duly saved in the file 
sample.txt
Adding //clearly addStream is being called
Instance of 'Future<int>' //I had expected to see the length of the available data here
closed //close is clearly being called BUT
Finished! //back in main()

我对这里潜在问题的理解仍然相当有限。我的期待

  1. 我原以为我会使用 addStream 来累积正在下载的内容,直到
  2. 此时将调用 close 并且程序将显示 exited

为什么 addStream 显示的是 instance of... 而不是可用内容的长度? 尽管 VSCode 调试控制台确实显示 exited,但这会在显示 closed 几秒钟后发生。我认为这可能是必须调用 super.close() 的问题,但事实并非如此。我在这里做错了什么?

解决方法

我本打算删除这个问题,但为了让其他尝试做类似事情的人受益,我决定将它留在这里并提供一个答案。

要注意的关键点是对 Accumulator.addStream 的调用就是这样做的 - 它提供了一个流以供 listened 使用,没有要读取的实际数据。你接下来要做的是

void whenData(List<int> data)
{
 //you will typically get a sequence of one or more bytes here.
 for(int value in data)
 {
  //accumulate the incoming data here
 } 
 return;
} 

function void whenDone()
{
 //now that you have all the file data *accumulated* do what you like with it
} 

@override
Future<void> addStream(Stream<List<int>> s) async
{
 s.listen(whenData,onDone:whenDone);
 //you can optionally ahandler for `onError`
}