如何在 Dart 中为 MultiPart 请求设置超时?

Posted

技术标签:

【中文标题】如何在 Dart 中为 MultiPart 请求设置超时?【英文标题】:How to set Timeout for MultiPart Request in Dart? 【发布时间】:2020-02-03 10:10:33 【问题描述】:

这是我的MultiPartRequest 代码

var request =
            http.MultipartRequest("POST", Uri.parse(EMPLOYEE_PUNCH_IN_URL));

        request.fields['uid'] = userId;
        request.fields['location'] = location;
        request.fields['punchin_time'] = punchInTime;
        request.fields['punchin_location_name'] = address;

        var multiPartFile = await http.MultipartFile.fromPath(
            "photo", imageFile.path,
            contentType: MediaType("image", "$extension"));
        request.files.add(multiPartFile);
        http.StreamedResponse response = await request.send();

        var responseByteArray = await response.stream.toBytes();

        employeePunchInModel = standardSerializers.deserializeWith(
            EmployeePunchInModel.serializer,
            json.decode(utf8.decode(responseByteArray)));
        ......

我知道如何将超时设置为正常的 http 请求。我已经关注了这个链接

Set timeout for HTTPClient get() request

我尝试通过以下方式添加超时功能,但它不起作用并且我的请求已完成

1.

 var multiPartFile = await http.MultipartFile.fromPath(
            "photo", imageFile.path,
            contentType: MediaType("image", "$extension")).timeout(const Duration(seconds: 1)); 

2.

http.StreamedResponse response = await request.send().timeout(const Duration(seconds: 1));

3.

var responseByteArray = await response.stream.toBytes().timeout(const Duration(seconds: 15));

但上述超时都不起作用。

【问题讨论】:

选项 1 设置从文件构建多部分请求的超时。所以如果例如。它是一个需要很长时间才能读取的大文件或设备 IO 很慢,它会在构建请求之前超时。选项 2 设置接收响应标头的超时。这通常意味着服务器已经接收并处理了整个请求,并且已经响应了一个状态码。选项 3 设置接收其余响应的超时时间 - 响应正文。你到底想设置什么超时?? @Ovidiu 我想为发送到服务器的 http 请求设置超时 那是选项 2。如果服务器在给定时间内没有接收、读取和处理整个请求,然后回复返回给应用程序的 HTTP 状态代码,则将被视为超时。 这是关于 C# 的吗? @Momoro 关于 Dart 语言和 Flutter 框架 【参考方案1】:

使用http package,这是我的方法:

    创建我们将用于onTimeOut 回调的流式响应
StreamedResponse timeOutResponse(
  @required String httpMethod,
  @required dynamic error,
  @required String url,
)  
  Map<String, dynamic> body = 
    'any': 'value',   
    'you': 'want for $error',
  ;

  int statusCode = 404;  
  Uri destination = Uri.parse(url);
  String json = jsonEncode(body);
  
  return StreamedResponse(
    Stream.value(json.codeUnits),
    statusCode,
    request: Request(httpMethod, destination),
  );

    使用Mahesh Jamdade答案中修改后的http multipart函数
Future<http.Response> makeAnyHttpRequest(String url,
  Map<String, dynamic> body,
  Function onTimeout,
  Duration duration = const Duration(seconds: 10)) async 

     final request = http.MultipartRequest(
         'POST',
          Uri.parse('$url'),
       );
     
     final res = await request.send().timeout(
         duration,
         onTimeout: () 
           return timeOutResponse(
             httpMethod: 'MULTIPART POST',
             error: 'Request Time Out',
             url: url,
           );
         ,
       );

     return await http.Response.fromStream(res);
  

这样,您可以返回onTimeOut Http Response,而不是超时异常。

【讨论】:

【参考方案2】:

你可以试试这个使用http package

用你想要的参数声明你的多部分函数

 Future<http.Response> makeAnyHttpRequest(String url,
      Map<String, dynamic> body,
      Function onTimeout,
      Duration duration = const Duration(seconds: 10)) async 
    final request = http.MultipartRequest(
      'POST',
      Uri.parse('$url'),
    );
    final res = await request.send().timeout(duration, onTimeout: onTimeout);
    return await http.Response.fromStream(res);
  

然后在 try catch 块中调用它,您可以通过在 Timeout 上抛出所需的值来捕获超时异常。

try
    final res = makeAnyHttpRequest("<url>","body":"here",onTimeout:()
      throw 'TIME_OUT'; // Throw anything
    );
      
  catch(_)
      if (_.toString() == 'TIME_OUT')  // catch the thrown value to detect TIMEOUT
        /// DO SOMETHING ON TIMEOUT    
          debugPrint('The request Timeout');   
     
  

只要您有onTimeout 回调,上述方法适用于任何http 请求

【讨论】:

【参考方案3】:

我建议

var request = http.MultipartRequest("POST", Uri.parse(EMPLOYEE_PUNCH_IN_URL));

  request.fields['uid'] = userId;
  request.fields['location'] = location;
  request.fields['punchin_time'] = punchInTime;
  request.fields['punchin_location_name'] = address;

  var multiPartFile = await http.MultipartFile.fromPath(
      "photo", imageFile.path,
      contentType: MediaType("image", "$extension"));
  request.files.add(multiPartFile);

  await request.send().timeout(Duration(seconds: 1), onTimeout: () 
    throw "TimeOut";
  ).then((onValue) 
    var responseByteArray = await onValue.stream.toBytes();

    employeePunchInModel = standardSerializers.deserializeWith(
        EmployeePunchInModel.serializer,
        json.decode(utf8.decode(responseByteArray)));
  ).catchError(() throw "TimeOut";);

【讨论】:

会试一试 我收到一个错误firebasestorage.googleapis.com/v0/b/… @Nudge ..我编辑了 var responseByteArray = await onValue.stream.toBytes(); 不起作用。我试过你的解决方案。正如您在我在问题中提到的第二步中看到的那样,我使用的是您描述的相同解决方案。也许超时不适用于多部分 http。我想需要提交一个错误【参考方案4】:

使用Dio 包和以下代码:

  try 
  final response = await Dio().post(requestFinal.item1, data:formData, options: option,
      onSendProgress: (sent, total) 
        print("uploadFile $sent / total");
      );

  print("Response Status code:: $response.statusCode");

  if (response.statusCode >= 200 && response.statusCode < 299) 

    dynamic jsonResponse = response.data;
    print("response body :: $jsonResponse");
    final message = jsonResponse["msg"] ?? '';
    final status = jsonResponse["status"] ?? 400;
    final data = jsonResponse["data"];
    return HttpResponse(status: status, errMessage: message, json: data);
  
  else 
    dynamic jsonResponse = response.data;
    print('*********************************************************');
    print("response body :: $jsonResponse");
    print('*********************************************************');
    var errMessage = jsonResponse["msg"];
    return HttpResponse(status: response.statusCode, errMessage: errMessage, json: jsonResponse);
  

on DioError catch(error) 
  print('*********************************************************');
  print('Error Details :: $error.message');
  print('*********************************************************');
  dynamic jsonResponse = error.response.data;
  print('*********************************************************');
  print("response body :: $jsonResponse");
  print('*********************************************************');
  var errMessage = jsonResponse["message"] ?? "Something went wrong";
  return HttpResponse(status: jsonResponse["status"] , errMessage:  errMessage, json: null);
                                                                                      

希望这会有所帮助!

【讨论】:

我知道 Diorites 库,但我不能使用它,因为我的办公室项目和我的团队领导不允许它使用【参考方案5】:

你也可以使用 dio 3.0.4

一个强大的 Dart Http 客户端,支持拦截器、全局配置、FormData、请求取消、文件下载、超时等。

这是链接:Http client for Dart

【讨论】:

以上是关于如何在 Dart 中为 MultiPart 请求设置超时?的主要内容,如果未能解决你的问题,请参考以下文章

带有 multipart/form-data 的请求返回 415 错误

如何在 Dart 中为伪元素设置 css 属性

在 Android Studio 中为 Dart 启用彩虹括号

如何在颤动中为整个应用程序设置文本颜色主题

在单个 TextField 中为多行下划线 - Flutter/Dart

在 VS Code 中为 dart 禁用自动格式化