将AWS S3响应序列化到Stream时触发ReadTimeout InvalidOperationException异常
问题触发原因
- 该异常和AWS S3的
GetObjectAsync调用、流复制逻辑无关,错误实际发生在接口返回值序列化阶段:你的代码直接将MemoryStream类型作为接口返回值返回,ASP.NET Core默认的System.Text.Json序列化器会遍历序列化MemoryStream的所有公共属性,其中包含ReadTimeout/WriteTimeout属性,而MemoryStream本身不支持超时操作,访问这两个属性就会抛出InvalidOperationException。 - 你观察到的S3相关超时是误判,整个S3文件读取、流写入MemoryStream的逻辑在报错之前就已经执行完成。
解决方案
根据你的接口返回需求选择对应方案即可:
场景1:接口需要提供文件下载能力
不要直接返回MemoryStream对象,改为返回ASP.NET Core内置的FileResult类型,序列化器不会对该类型做Json序列化,而是直接输出二进制流:
using (GetObjectResponse response = await amazonS3Client.GetObjectAsync(request)) { MemoryStream memoryStream = new MemoryStream(); using (Stream responseStream = response.ResponseStream) { // 建议用异步CopyToAsync避免阻塞 await responseStream.CopyToAsync(memoryStream); } // 必须重置流的位置到起始点,否则读取到的内容为空 memoryStream.Position = 0; // 可以直接复用S3返回的ContentType,也可以手动指定 return File(memoryStream, response.Headers.ContentType, "自定义下载文件名"); }
如果文件体积较大,还可以省略内存复制步骤,直接将S3的响应流作为返回内容,减少内存占用:
using (GetObjectResponse response = await amazonS3Client.GetObjectAsync(request)) { return File(response.ResponseStream, response.Headers.ContentType, "自定义下载文件名"); }
场景2:接口需要返回JSON格式,同时携带文件内容
不要将MemoryStream放到返回对象中,转为将文件内容转成Base64字符串后再返回:
using (GetObjectResponse response = await amazonS3Client.GetObjectAsync(request)) { MemoryStream memoryStream = new MemoryStream(); using (Stream responseStream = response.ResponseStream) { await responseStream.CopyToAsync(memoryStream); } return new { Code = 200, FileName = "你的文件名", FileBase64 = Convert.ToBase64String(memoryStream.ToArray()) }; }
内容的提问来源于stack exchange,提问作者waching
相关产品推荐
相关产品推荐

