项目使用laravel开发,用bref部署到aws的lambda上,有一个上传文件的接口,由于lambda限制请求和响应大小为6mb,导致上传大于6mb的文件失败
这时候可以使用s3的presign post让前端直接上传到s3,presign post比presign url多了一个policy功能,你可以限制上传的文件大小,位置
$client = new S3Client([
'version' => 'latest',
'region' => config('filesystems.disks.s3.region'),
]);
$bucket = config('filesystems.disks.s3.bucket');
$temp_path = 'uploads/temp/images';
$options = [
['bucket' => $bucket],
['content-length-range' ,0, 20000000], //这里限制上传文件大小,超出大小s3会返回错误
['starts-with' ,'$key',$temp_path . '/'], //限制上传到指定目录,你也可以直接指定上传到那个文件夹,文件名
//['eq', '$Content-Type', 'image/jpeg'], //限制上传文件类型
];
$postObject = new PostObjectV4(
$client,
$bucket,
[],
$options,
);
return response()->json([
'attr' => $postObject->getFormAttributes(),
'input' => array_merge($postObject->getFormInputs(),[
'key'=>$temp_path . '/' . Carbon::now()->format('Y-m-d').'/'.$request->post('name')
])
]);
示例响应
{
"attr": {
"action": "https://cxx.s3.ap-southeast-1.amazonaws.com",
"method": "POST",
"enctype": "multipart/form-data"
},
"input": {
"key": "uploads/temp/posts/images/2023-05-04/Screenshot 2023-05-03 at 11.03.33.png",
"X-Amz-Credential": "AKIAV7ASRH6Z642DM35C/20230504/ap-southeast-1/s3/aws4_request",
"X-Amz-Algorithm": "AWS4-HMAC-SHA256",
"X-Amz-Date": "20230504T131615Z",
"Policy": "eyJleHBpcmF0aW9uIjoiMjAyMy0wNS0wNFQxNDoxNjoxNVoiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJmYmJ1Y2NrZXQxMjMifSxbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwwLDEwMDAwMDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVwbG9hZHNcL3RlbXBcL3Bvc3RzXC9pbWFnZXNcLzIwMjMtMDUtMDRcLyJdLHsiWC1BbXotRGF0ZSI6IjIwMjMwNTA0VDEzMTYxNVoifSx7IlgtQW16LUNyZWRlbnRpYWwiOiJBS0lBVjdBU1JINlo2NDJETTM1Q1wvMjAyMzA1MDRcL2FwLXNvdXRoZWFzdC0xXC9zM1wvYXdzNF9yZXF1ZXN0In0seyJYLUFtei1BbGdvcml0aG0iOiJBV1M0LUhNQUMtU0hBMjU2In1dfQ==",
"X-Amz-Signature": "82120e8d2dd1515cb18dc9a39810d5aca682f4fbbd5cc5b13a1d28007e29dd4e"
}
}
前端接受到参数后,构建post请求到s3的bucket即可
const {attr, input} = response;
const amzForm = new FormData()
Object.keys(input).forEach(k => {
amzForm.append(k, input[k])
})
amzForm.append('file', fileToUpload)
return axios.request({
url: attr.action,
method: 'post',
headers: {"Content-Type": "multipart/form-data"},
data: amzForm,
})(attr.action, amzForm).then(r => {
//这里是上传成功,可以通知后端
}).catch(r => {
console.log(r)
})
这里如果上传成功,s3会给一个204的响应,如果失败了,会给一个400的响应,格式是xml
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>EntityTooLarge</Code>
<Message>Your proposed upload exceeds the maximum allowed size</Message> //可以看到错误原因是文件太大
<ProposedSize>10002748</ProposedSize>
<MaxSizeAllowed>10000000</MaxSizeAllowed>
<RequestId>XBPNJFVJP7MR9SMD</RequestId>
<HostId>Kg7cWw=</HostId>
</Error>
还有一个问题是即使你在签名时指定了content-type限制,['eq', '$Content-Type', 'image/jpeg'],s3只会验证你的form的content-type字段的值是不是image/jpeg,至于你上传的文件的mime type是不是image/jpeg,s3是不会去验证的