- 前端要上传文件,找后端要签名
- 后端将签名给到前端,签名里面可以设置各种参数(上传目录,文件大小,类型,上传有效期,回调等)
- 前端拿到这些参数签名后,构造 `from` 表单,将相应的 `key`,`value` 赋值后上传到指定的URL上
- 上传成功后,S3会根据签名里面设置的回调路由或状态码给到前端,前端来判断成功或失败(也可以回调到指定的后端接口来判断是否成功)
## 图解:

1. 用户从 Web 浏览器访问您的页面。
1. 您的网页包含一个 HTML 表单,其中包含用户将内容上传到 Amazon S3 所需的所有信息。
1. 用户通过 Web 浏览器将内容上传到 Amazon S3。
## 验签图解

# 附录-代码示例
## 参考文档
参考文档在代码里面有,详情看代码
## 服务端代码
* Description: 后端签名,前端直传S3
* Author: Shuxiaoyuan
* Email:
[email protected]
* DateTime: 2022/5/10 15:05
* @param Request $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
* @throws \Exception
public function testS3UploadHtml(Request $request)
// 一个是前端 HTML 表单中需要的字段,一个是后端 policy 的验签
// HTML 表单里面的字段,都由后端 policy 验签中获得
// HTML 表单字段:https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/sigv4-HTTPPOSTForms.html
$html_form = [
// 非必须,默认 private
'acl' => 'private',
// 必须,上传文件的键名,如果要使用用户提供的文件本身的文件名,请使用 ${filename} 变量,如:/data/user1/${filename}
'key' => '/data/s3/' . date('Ymd') . '/' . Str::random(32),
// base64编码的安全策略,描述请求中允许的内容。
// 没有安全策略的请求被认为是匿名的,并且只能在可公开写入的bucket上成功。
'policy' => '',
// 非必须:成功上载后客户端重定向到的URL。
'success_action_redirect' => 'https://www.shuxiaoyuan.com/',
// 非必须,如果未指定 success_action_redirect 则在成功上载时返回给客户端的 http 状态码,有效值为200、201或204(默认值)。
'success_action_status' => 200,
// 用于验证请求的签名算法。对于 AWS 签名版本4,值为 AWS4-HMAC-SHA256
'x-amz-algorithm' => 'AWS4-HMAC-SHA256',
// 用于计算签名的凭据。它提供访问密钥 ID 和范围信息,标识签名对其有效的区域和服务。这应该与您在计算签名计算的签名密钥时使用的范围相同。
// 它是以下形式的字符串:
////aws4_request
// 例如:AKIAIOSFODNN7EXAMPLE/20130728/us-east-1/s3/aws4_request
'x-amz-credential' => $this->key . '/' . date('Ymd') . '/' . $this->region . '/s3/aws4_request',
// ISO8601 格式字符串中指定的日期值。例如,20130728T000000Z。日期必须与您在创建签名计算签名密钥时使用的日期相同。
// 如果请求中包含 POST 策略文档,则这是必需的。
'x-amz-date' => '20220511T000000Z',
// 非必须:Amazon DevPay使用的安全令牌和会话凭据。更多看文档
'x-amz-security-token' => '',
// (AWS签名版本4)安全策略的HMAC-SHA256哈希。
'x-amz-signature' => '',
// 非必须:以该前缀开头的字段名是用户定义的元数据。每一个都作为一组键值对存储和返回。Amazon S3不验证或解释用户定义的元数据。
// 更多详细,看文档
'x-amz-meta-*' => '',
// 非必须:请参见POST对象(其他x-amz-*标题请参见POST对象)。
'x-amz-*' => '',
// 必须:文件或文本内容。文件或内容必须是表单中的最后一个字段。一次不能上载多个文件。
'file' => '',
// POST 策略:https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
$post_policy = [
// 非必须:指定表单提交中必须使用的 ACL 值。默认 private,需要存储桶开启 acl
'acl' => 'private',
// 桶:指定可接受的存储桶名称。
'bucket' => $this->bucket,
// 上传内容的最小和最大允许大小,示例 1M~100M
'content-length-range' => ["content-length-range", 1024 * 1024, 1024 * 1024 * 100],
// 上传文件,可以是目录,可以是自定义文件名,自定义需要用变量代替
'key' => 'images/' . date('Ymd') . '/' . Str::random('32'),
// 成功上传后客户端重定向到的 URL。
'success_action_redirect' => 'https://www.shuxiaoyuan.com/',
// success_action_redirect 如果不指定,上传成功返回给客户端的状态码,默认 204
'success_action_status' => 200,
// 签名计算时必须使用的签名算法。对于 AWS 签名版本 4,该值为 AWS4-HMAC-SHA256
'x-amz-algorithm' => 'AWS4-HMAC-SHA256',
// 用于计算签名的凭据。它提供访问密钥 ID 和范围信息,标识签名对其有效的区域和服务。这应该与您在计算签名计算的签名密钥时使用的范围相同。
// 它是以下形式的字符串:////aws4_request
// 例如:AKIAIOSFODNN7EXAMPLE/20130728/us-east-1/s3/aws4_request
'x-amz-credential' => $this->key . '/' . date('Ymd') . '/' . $this->region . '/s3/aws4_request',
// ISO8601 格式字符串中指定的日期值。例如,20130728T000000Z。日期必须与您在创建签名计算签名密钥时使用的日期相同。
// 如果请求中包含 POST 策略文档,则这是必需的。
'x-amz-date' => '20220511T000000Z',
// Amazon DevPay 安全令牌。
'x-amz-security-token' => '',
// 用户指定的元数据。
'x-amz-meta-*' => '',
// 请参阅 POST 对象(其他 标头的POST 对象x-amz-*
'x-amz-*' => '',
// 签名规则:https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html
// 签名示例:https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
// 签名第一步:组装签名字符串(post 策略)
$time = time();
// 拼接 x-amz-credential
$credential_params = [
$this->key,
gmdate('Ymd', $time),
$this->region,
's3',
'aws4_request',
$credential = implode('/', $credential_params);
// 这样就是指定了文件名,如果不指定文件名,想用文件本身的文件名,就是 images/20220606/${filename}
$key = 'images/' . date('Ymd') . '/' . Str::random('32');
// POST 策略
$policy = [
'expiration' => gmdate('Y-m-d\TH:i:s\Z', strtotime('+2 hours', $time)),
'conditions' => [
['bucket' => $this->bucket],
['starts-with', '$key', $key],
['x-amz-credential' => $credential],
['x-amz-algorithm' => 'AWS4-HMAC-SHA256'],
['x-amz-date' => gmdate('Ymd\THis\Z', $time)],
// 手动签名第二步:组装签名key
$dateKey = hash_hmac("sha256", $credential_params[1], 'AWS4' . $this->secret, true);
$dateRegionKey = hash_hmac("sha256", $this->region, $dateKey, true);
$dateRegionServiceKey = hash_hmac("sha256", $credential_params[3], $dateRegionKey, true);
$signingKey = hash_hmac("sha256", $credential_params[4], $dateRegionServiceKey, true);
// 手动签名第三步:签名
$stringToSign = base64_encode(json_encode($policy));
$signature1 = hash_hmac("sha256", $stringToSign, $signingKey);
$signature = $signature1;
// 前端 from 表单需要的数据
$data = [
// 亚马逊中国上传地址:https://桶名.s3.区域(一般为cn-north-1).amazonaws.com.cn/
// 亚马逊全球上传地址:https://桶名.s3.amazonaws.com/
'url' => 'https://' . $this->bucket . '.s3.' . $this->region . '.amazonaws.com.cn/',
'bucket' => $this->bucket,
'key' => $key,
'x_amz_credential' => $credential,
'x_amz_algorithm' => 'AWS4-HMAC-SHA256',
'x_amz_date' => gmdate('Ymd\THis\Z', $time),
'policy_base64' => $encodedPolicy,
'signature' => $signature,
return view('s3.upload', ['data' => $data]);
## HTML 代码