[英]403 when upload file to S3 bucket using axios
我正在使用 axios 將音頻文件上傳到 AWS s3 存儲桶。
工作流是:React => AWS API Gateway => Lambda。
這是生成 S3 預簽名 URL 的后端代碼 Lambda:
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(AUDIO_S3_BUCKET)
.key(objectKey)
.contentType("audio/mpeg")
.build();
PutObjectPresignRequest putObjectPresignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10))
.putObjectRequest(putObjectRequest)
.build();
PresignedPutObjectRequest presignedPutObjectRequest = s3Presigner.presignPutObject(putObjectPresignRequest);
AwsProxyResponse awsProxyResponse = new AwsProxyResponse();
awsProxyResponse.setStatusCode(HttpStatus.SC_OK);
awsProxyResponse.setBody(
GetS3PresignedUrlResponse.builder()
.s3PresignedUrl(presignedPutObjectRequest.url().toString())
.build().toString());
return awsProxyResponse;
這是創建存儲桶的 java 代碼:
private void setBucketCorsSettings(@NonNull final String bucketName) {
s3Client.putBucketCors(PutBucketCorsRequest.builder()
.bucket(bucketName)
.corsConfiguration(CORSConfiguration.builder()
.corsRules(CORSRule.builder()
.allowedHeaders("*")
.allowedMethods("GET", "PUT", "POST")
.allowedOrigins("*") // TODO: Replace with domain name
.exposeHeaders("ETag")
.maxAgeSeconds(3600)
.build())
.build())
.build());
log.info("Set bucket CORS settings successfully for bucketName={}.", bucketName);
}
在我的前端,這是嘗試上傳文件的部分:
const uploadFile = (s3PresignedUrl: string, file: File) => {
let formData = new FormData();
formData.append("file", file);
formData.append('Content-Type', file.type);
const config = {
headers: {
"Content-Type": 'multipart/form-data; boundary=---daba-boundary---'
//"Content-Type": file.type,
},
onUploadProgress: (progressEvent: { loaded: any; total: any; }) => {
const { loaded, total } = progressEvent;
let percent = Math.floor((loaded * 100) / total);
if (percent < 100) {
setUploadPercentage(percent);
}
},
cancelToken: new axios.CancelToken(
cancel => (cancelFileUpload.current = cancel)
)
};
axios(
{
method: 'post',
url: s3PresignedUrl,
data: formData,
headers: {
"Content-Type": 'multipart/form-data; boundary=---daba-boundary---'
}
}
)
.then(res => {
console.log(res);
setUploadPercentage(100);
setTimeout(() => {
setUploadPercentage(0);
}, 1000);
})
.catch(err => {
console.log(err);
if (axios.isCancel(err)) {
alert(err.message);
}
setUploadPercentage(0);
});
};
但是,當嘗試上傳文件時,它返回 403 錯誤。
如果我使用 fetch 而不是 axios 並且它可以工作,就像這樣:
export async function putToS3(presignedUrl: string, fileObject: any) {
const requestOptions = {
method: "PUT",
headers: {
"Content-Type": fileObject.type,
},
body: fileObject,
};
//console.log(presignedUrl);
const response = await fetch(presignedUrl, requestOptions);
//console.log(response);
return await response;
}
putToS3(getPresignedUrlResponse['s3PresignedUrl'], values.selectdFile).then(
(putToS3Response) => {
console.log(putToS3Response);
Toast("Success!!", "File has been uploaded.", "success");
}
);
在我看來,這兩者之間的唯一區別是:當使用fetch
時,請求的Content-Type
header 是Content-Type: audio/mpeg
,但是當使用 axios 時,它是Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryClLJS3r5Xetv3rN7
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryClLJS3r5Xetv3rN7
。
如何讓它與 axios 一起使用? 我正在切換到 axios,因為它能夠監控請求進度,因為我想顯示上傳進度條。
我關注了這個博客,但不確定我錯過了什么: https://bobbyhadz.com/blog/aws-s3-presigned-url-react
您沒有將任何答案標記為已接受,所以我猜您沒有解決它。
對於那里的任何未來觀眾。 您收到403 forbidden
錯誤的原因是您的服務器端和客戶端中的Content-Type
不匹配。 我假設您正確設置了 AWS 策略。
您在后端的代碼應如下所示:
const presignedPUTURL = s3.getSignedUrl("putObject", {
Bucket: "bucket-name",
Key: String(Date.now()),
Expires: 100,
ContentType: "image/png", // important
});
在前端(假設您使用的是 axios):
const file = e.target.files[0]
const result = await axios.put(url, file, {
withCredentials: true,
headers: { "Content-Type": "image/png" },
});
實際上,您通常必須發送文件類型以在POST
正文或其他內容中生成預簽名的 url,然后在 axios 中執行file.type
以獲取上傳文件的文件類型。
您在 axios 中使用 POST。 應該改為 PUT。
此外,我認為內容類型必須與請求預簽名 URL 期間指定的內容類型相匹配,正如您正確指出的那樣,這是audio/mpeg
。
相應地,您的data
應該只是file
,而不是formData
。
axios(
{
method: 'put',
url: s3PresignedUrl,
data: file,
headers: {
"Content-Type": 'audio/mpeg'
}
}
...
檢查您的 Lambda 執行角色。 它可能是罪魁禍首。 也許它沒有授予足夠的權限來允許將文件放入您的存儲桶。
URL signing is a delegation of power on behalf of the signer, which is restricted to a specified object, action... Signing does not magically grants full read/write permissions on S3, even on the specific object related to the presigned URL.
生成簽名的“用戶”需要足夠的權限以允許您希望通過該預簽名的 URL 委派的操作。 在這種情況下,這是您的 Lambda function 的執行角色。
您可以將AmazonS3FullAccess
托管策略添加到執行角色,看看它是否能解決您的情況。 經過幾天的奮斗,這種變化使我擺脫了困境。 之后,在投入生產之前,將該規則限制為您希望允許上傳到的特定存儲桶(最小權限原則)。
如果您使用 SAM 本地仿真進行開發,那么只要您在本地運行函數,這些執行角色似乎就不會被考慮在內; 即使沒有 S3 權限,簽名的鏈接也可以在該上下文中工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.