[英]Pre-signed Post Uploads to S3 fails intermittently in Safari when uploading file via multipart/form-data content type
I've been experiencing pre-signed post upload failures to S3 that I cannot reproduce.我一直在遇到无法重现的 S3 的预签名后上传失败。 The failure happens mostly via Mobile Safari browsers, but my error log has shown that the error occurs on Desktop Safari as well.
故障主要通过移动 Safari 浏览器发生,但我的错误日志显示桌面 Safari 也发生错误。
When the error occurs, I get the following error back from S3:发生错误时,我从 S3 收到以下错误:
<Error>
<Code>EntityTooSmall</Code>
<Message>Your proposed upload is smaller than the minimum allowed size</Message>
<ProposedSize>0</ProposedSize>
<MinSizeAllowed>26254270</MinSizeAllowed>
<...></...>
</error>
The code I'm using to presign the S3 Url and subsequently upload the file works fine in other browsers, eg Chrome, Firefox, Brave.我用来预签名 S3 Url 并随后上传文件的代码在其他浏览器中运行良好,例如 Chrome、Firefox、Brave。
Once I have the pre-signed URL, I'm creating a new FormData
object and using the pre-signed fields to construct an object which I then send to S3 from the browser via Axios. Once I have the pre-signed URL, I'm creating a new
FormData
object and using the pre-signed fields to construct an object which I then send to S3 from the browser via Axios.
My guess is the error has something to do with Safari incorrectly appending the file data to the FormData object, but since it happens randomly, I can't figure out why.我的猜测是该错误与 Safari 错误地将文件数据附加到 FormData object 有关,但由于它是随机发生的,我不知道为什么。
Safari correctly reads the size of the file, as this value is used to create the presigned url, but when it comes time to attach it to the form data object and upload, something must be going wrong. Safari 正确读取了文件的大小,因为该值用于创建预签名的 url,但是当需要将其附加到表单数据 object 时,一定会出错并上传。
The following code is what I use with success on other browsers, and what works most of the time on safari:以下代码是我在其他浏览器上成功使用的代码,并且在 safari 上大部分时间都有效:
<script>
import axios from 'axios'
import getPresignUrl from './presign'
// Vue Component
export default {
name: 'FinickyOnSafariUploader'
data: () => ({
// ...
}),
methods: {
presignAndUpload (file) {
const fileData = {
fileName: file.name,
fileType: file.type,
fileSize: file.size
}
getPresignUrl(fileData)
.then((resp) => {
const presignFields = resp.data.presignFields // Object
const presignUrl = resp.data.presignUrl // String
return this.uploadToS3(file, presignUrl, presignUrl, presignFields)
})
},
uploadToS3 (file, presignUrl, presignFields) {
formPostData = new FormData()
// Add Presigned Post Fields for Auth
for (const field in presignFields) {
formPostData.append(field, presignFields[field])
}
// Add Content-Type & File Data
formPostData.append('Content-Type', file.type)
formPostData.append('file', file)
const req = {
url: presignUrl,
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' },
data: formPostData
}
return axios.request(req)
}
}
}
</script>
Does anyone have any guidance/insight as to why this happens sporadically and only on Safari?有没有人对为什么这种情况偶尔发生并且仅在 Safari 上发生有任何指导/见解?
It looks like you need to wait for the methods that take a long time to finish, to finish doing what they need to do.看起来您需要等待需要很长时间才能完成的方法,才能完成他们需要做的事情。 You can do this with promises or using async / await.
你可以使用 Promise 或使用 async / await 来做到这一点。 I suspect you would be able to reproduce this error on any browser if you upload a big enough file, that would make your functions return nothing instead of something when they are done.
我怀疑如果您上传足够大的文件,您将能够在任何浏览器上重现此错误,这将使您的函数在完成后什么也不返回。
You can use async / await on object methods like:您可以在 object 方法上使用 async / await,例如:
//First lets reproduce suspected error
const axios = require("axios");
const objError = {
somefunction() {
return axios.get("https://source.unsplash.com/random");
},
};
console.log(objError.somefunction());
//the fix
const objFix = {
async somefunction() {
return await axios.get("https://source.unsplash.com/random");
},
};
fixed = async () => {
console.log(await objFix.somefunction());
};
fixed();
To convert your methods to do their job asynchronously and then wait for them you need to do something like:要将您的方法转换为异步完成工作,然后等待它们,您需要执行以下操作:
export default {
//...
methods: {
async presignAndUpload(file) {
await getPresignUrl(fileData).then((resp) => {
//...
return await this.uploadToS3(file, presignUrl, presignUrl, presignFields);
});
},
async uploadToS3(file, presignUrl, presignFields) {
//...
return await axios.request(req);
},
},
};
Good evening) I think you should use async/await.晚上好)我认为你应该使用 async/await。 I suppose your functions execute without waiting for finish.
我想您的功能无需等待完成即可执行。 Write like
像这样写
async function doUpload() {
await operation1();
await operation2();
await operation3();
}
it can guarantee what all your functions executed correctly它可以保证您的所有功能都正确执行
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.