为啥在多部分上传到在 aws lambda 中运行的 express 过程中文件被损坏?
Posted
技术标签:
【中文标题】为啥在多部分上传到在 aws lambda 中运行的 express 过程中文件被损坏?【英文标题】:Why is file being corrupted during multipart upload into express running in aws lambda?为什么在多部分上传到在 aws lambda 中运行的 express 过程中文件被损坏? 【发布时间】:2018-12-02 00:45:21 【问题描述】:拥有一个带有 redux 客户端和 express webapi 的 SPA。用例之一是将单个文件从浏览器上传到快速服务器。 Express 使用multer
中间件对上传的文件进行解码,并将其放入req
对象的数组中。在 localhost 上运行时一切正常。
但是,当应用程序部署到 AWS 时,它无法按预期运行。部署将 express api 推送到 AWS Lambda 函数,redux 客户端静态资产由 Cloudfront CDN 提供服务。在那种环境中,上传的文件确实会到达 express 服务器,由 multer 处理,并且文件最终会成为 req.files
数组中预期的第一个(也是唯一一个)项目。
问题是文件包含错误的字节。例如,当我上传一个长度为 2795 字节的示例图像时,最终被 multer 解码的文件长度为 4903 字节。在 multer 解码并将它们放入 req.files 数组时,我尝试过的其他图像总是以大致相同的倍数变大。因此,文件已损坏且未显示为图像。
文件上传如下:
<input type="file" name="files" onChange=this.onUploadFileSelected />
...
onUploadFileSelected = (e) =>
const file = e.target.files[0]
var formData = new FormData()
formData.append("files", file)
axios.post('to the url', formData, withCredentials: true )
.then(handleSuccessResponse).catch(handleFailResponse)
我已尝试使用 MemoryStorage 和 DiskStorage 设置 multer。两者都可以在 localhost 和 aws lambda 上工作,但是两者都表现出相同的行为——文件更大并且在存储中已损坏。
我还尝试将 multer 设置为全局中间件(通过app.use
)和上传路由上的特定于路由的中间件(通过routes.post('the url', multerMiddlware, controller.uploadAction)
。同样,两者都表现出相同的行为。配置了 Multer 中间件像这样:
const multerMiddleware = multer(/* optionally set dest: '/tmp' */)
.array('files')
一个区别是,在 localhost 上,客户端和 express 都通过 http 提供服务,而在 aws 中,客户端和 express 都通过 https 提供服务。我不相信这会有所作为,但我还无法测试——要么通过 https 运行 localhost,要么通过 http 在 aws 中运行。
我注意到的另一件奇怪的事情是,当 multer 中间件存在时,其他中间件似乎无法按预期运行。而不是next()
函数将流程向下移动到控制器动作,相反,其他中间件将在控制器动作调用之前完全退出,并且当控制器调用退出时,控制不会在next()
调用之后流回中间件。删除 multer 中间件后,其他中间件会按预期运行。然而,这个观察是在 localhost 上进行的,整个端到端用例确实按预期运行。
当部署到云端而不是本地主机上时,什么可能会弄乱上传的图像文件有效负载?难道真的是 https 造成了影响?
更新 1
当我上传this file(11228 字节)
这是 HAR chrome 为我提供的本地(预期)文件上传:
"postData":
"mimeType": "multipart/form-data; boundary=----WebKitFormBoundaryC4EJZBZQum3qcnTL",
"text": "------WebKitFormBoundaryC4EJZBZQum3qcnTL\r\nContent-Disposition: form-data; name=\"files\"; filename=\"danludwig.png\"\r\nContent-Type: image/png\r\n\r\n\r\n------WebKitFormBoundaryC4EJZBZQum3qcnTL--\r\n"
这是 HAR chrome 为我提供的 aws(损坏的)文件上传:
"postData":
"mimeType": "multipart/form-data; boundary=----WebKitFormBoundaryoTlutFBxvC57UR10",
"text": "------WebKitFormBoundaryoTlutFBxvC57UR10\r\nContent-Disposition: form-data; name=\"files\"; filename=\"danludwig.png\"\r\nContent-Type: image/png\r\n\r\n\r\n------WebKitFormBoundaryoTlutFBxvC57UR10--\r\n"
保存的损坏图像文件长度为 19369 字节。
更新 2
我创建了一个文本文件,其文本为 hello world
,长度为 11 个字节并上传了它。它不会在 aws 中损坏。即使我使用 txt 或 png 后缀上传它也是如此,它在持久化时最终长度为 11 个字节。
更新 3
尝试使用更大的文本文件(12132 字节长)进行上传,结果与更新 2 中的结果相同——文件完好无损,没有损坏。
【问题讨论】:
你能显示上传请求调用的har
吗?你也可以尝试上传一个带有一些文本的文本文件,看看它是如何被搞砸的吗?您有我们可以在 aws 上设置并测试的最小示例吗?
@TarunLalwani 从未生成过 har,正在研究它是否对 https 请求有用。这是一个关于上传文本文件的好主意,希望我能想到这一点。没有最小的例子(还没有),代码来自私有存储库。
@TarunLalwani 问题已更新。上传文本时不会出现此问题,无论它具有什么扩展名。还发布了成功和不成功请求的 har 摘录。
11
字节太小了。我建议有一个更大的文本文件进行测试,类似于图像大小,所以我们知道是否有任何类型的损坏
@TarunLalwani 我刚刚再次尝试使用 12132 字节长的文本文件。同时具有 .txt 和 .png 扩展名。同样的结果,在 aws 中,文件在持久化时正是该大小,并且在下载和查看时是相同的。
【参考方案1】:
可能的答案:
找到这个https://forums.aws.amazon.com/thread.jspa?threadID=252327
API Gateway 本身不支持多部分表单数据。它是 可以配置二进制直通然后处理这个多部分 集成中的数据(您的后端集成或 Lambda 函数)。
如果您在 AWS 中使用 API Gateway 事件来触发托管您的快速服务器的 lambda,您似乎需要另一种方法。
或者,您可以将 API Gateway 配置为使用每个 https://***.com/a/41770688/304832 的二进制有效负载
或者,直接从您的客户端上传到签名的 s3 网址(或公共网址)并使用它来触发另一个 lambda 事件。
在我们有机会尝试不同的 API 网关设置之前,我们找到了一个临时解决方法:使用 FileReader
将文件转换为 base64 文本字符串,然后提交。只要有效载荷是文本,上传似乎没有任何问题。
【讨论】:
不,我不得不对此进行时间限制,现在已经转移到其他事情上。在这里记录我的发现与团队分享,以防万一这被移交给其他人。截至目前,问题仍未解决/未回答以上是关于为啥在多部分上传到在 aws lambda 中运行的 express 过程中文件被损坏?的主要内容,如果未能解决你的问题,请参考以下文章