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

Flutter应用Google Drive可恢复上传:大文件无法获取会话URI

Flutter中Google Drive API断点续传POST请求大文件返回400错误

我在Flutter应用里基于Google Drive API实现了断点续传功能,用于上传大文件。流程需要先发送POST请求获取resumable session URI(响应的Location头),再用PUT请求上传文件内容。目前小文件(小于2MB)能正常获取会话URI,但大于2MB的文件POST请求直接返回400 Bad Request,多次测试问题依旧。

我的POST请求代码如下:

Uri uri = Uri.parse('https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable');
var authToken = headers['Authorization'];
String body = json.encode({
  'name' : fileName,
  'originalFilename' : URN,
  'parents' : [monthFolderId],
  'contentHints' : [{
    'thumbnail' : {
      'mimeType' : 'image/png',
      'image' : base64Image
    }
  }]
});

final initialStreamedRequest = new http.StreamedRequest('POST', uri)
  ..headers.addAll({
    'Authorization': authToken!,
    'X-Upload-Content-Length' : zipFileLength.toString(),
    'X-Upload-Content-Type' : 'application/octet-stream',
    'Content-Length' : utf8.encode(body).length.toString(),
    'Content-Type' : 'application/json; charset=UTF-8',
  });

initialStreamedRequest.sink.add(utf8.encode(body));
initialStreamedRequest.sink.close();

http.StreamedResponse response = await initialStreamedRequest.send()
    .onError((error, stackTrace) {
        return Future.error(error!);
});

String? sessionUri;
if (response.statusCode == 200) {
  sessionUri = response.headers['location'];
}

问题排查与修复建议

  1. 修正contentHints格式错误
    Google Drive API中contentHints是一个对象而非数组,你的代码里写成了数组[{...}],这会导致请求体JSON格式不符合API要求。小文件可能未触发严格校验,大文件则直接返回400。修改为:

    'contentHints': {
      'thumbnail': {
        'mimeType': 'image/png',
        'image': base64Image
      }
    }
    
  2. 移除手动设置的Content-Length头
    StreamedRequest会自动处理请求体的Content-Length计算,手动设置可能因编码细节导致长度不匹配,引发400错误。直接删除'Content-Length' : utf8.encode(body).length.toString(),这一行。

  3. 确保X-Upload-Content-Length准确
    确认zipFileLength是文件的真实字节数,转字符串时没有格式错误(比如不要包含非数字字符)。

  4. 查看400响应的详细错误信息
    添加代码获取响应体内容,Google API会返回具体错误原因,帮助精准定位:

    if (response.statusCode != 200) {
      String errorBody = await response.stream.bytesToString();
      print('Error details: $errorBody');
    }
    
  5. 确保请求体发送完整
    关闭sink时加上await,确保所有数据都已发送:

    await initialStreamedRequest.sink.close();
    

修改后的完整代码示例

Uri uri = Uri.parse('https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable');
var authToken = headers['Authorization'];
String body = json.encode({
  'name': fileName,
  'originalFilename': URN,
  'parents': [monthFolderId],
  'contentHints': {
    'thumbnail': {
      'mimeType': 'image/png',
      'image': base64Image
    }
  }
});

final initialStreamedRequest = http.StreamedRequest('POST', uri)
  ..headers.addAll({
    'Authorization': authToken!,
    'X-Upload-Content-Length': zipFileLength.toString(),
    'X-Upload-Content-Type': 'application/octet-stream',
    'Content-Type': 'application/json; charset=UTF-8',
  });

initialStreamedRequest.sink.add(utf8.encode(body));
await initialStreamedRequest.sink.close();

http.StreamedResponse response = await initialStreamedRequest.send()
    .onError((error, stackTrace) => Future.error(error!));

// 打印错误详情
if (response.statusCode != 200) {
  String errorBody = await response.stream.bytesToString();
  print('Error response: $errorBody');
}

String? sessionUri;
if (response.statusCode == 200) {
  sessionUri = response.headers['location'];
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:45:37