vue + axios 通过 S3 预签名地址上传文件

如果业务中用到了 S3,那文件的上传下载必然是最基础的功能,但如果直接通过 Upload 方法上传文件,会存在非常大的安全隐患,所以普遍做法是后端提供文件上传预签名地址,前端根据预签名地址进行文件上传操作。

一、后端生成预签名地址

根据后端语言的不同种类,使用不同的 SDK,这里示例采用  golang 生成 S3 文件上传预签名地址:

package main

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"time"
)

var (
	Client          *s3.S3
	AccessKeyId     = "xxx"
	SecretAccessKey = "xxx"
	Region          = "xxx"
)

type PutObjectInput struct {
	Acl           string
	StorageClass  string
	Tagging       string
	Expires       int64
	ContentType   string
	ContentLength int64
	ContentMd5    string
}

func main() {
	if err := InitS3Client(AccessKeyId, SecretAccessKey, Region); err != nil {
		fmt.Println("初始化 S3 操作的 session 错误", err)
		return
	}

	fmt.Println(PutObjectPreSign("test", "/test/a.jpg", 300, &PutObjectInput{
		Acl:           "public-read",
		ContentType:   "image/jpg",
		ContentLength: 12235,
	}))
}

func InitS3Client(accessKeyId, secretAccessKey, region string) error {
	// 初始化 S3 操作的 session
	sess, err := session.NewSession(&aws.Config{
		Credentials: credentials.NewStaticCredentials(accessKeyId, secretAccessKey, ""),
		Region:      aws.String(region),
	})
	if err != nil {
		return err
	}
	// 初始化 s3 操作客户端
	Client = s3.New(sess)
	return nil
}

func PutObjectPreSign(bucket, key string, ttl int64, info *PutObjectInput) (url string, err error) {
	// 1. 构建预处理信息
	input := s3.PutObjectInput{
		Bucket:               aws.String(bucket),
		Key:                  aws.String(key),
		ServerSideEncryption: aws.String("AES256"),
		ACL:                  aws.String(info.Acl),
		StorageClass:         aws.String(info.StorageClass),
		Tagging:              aws.String(info.Tagging),
		Expires:              aws.Time(time.Unix(info.Expires, 0)),
		ContentType:          aws.String(info.ContentType),
		ContentMD5:           aws.String(info.ContentMd5),
		ContentLength:        aws.Int64(info.ContentLength),
	}
	// 2. 组件预签名请求,获取预签名,默认 300s 过期
	req, _ := Client.PutObjectRequest(&input)
	if ttl == 0 {
		ttl = 300
	}
	url, _, err = req.PresignRequest(time.Duration(ttl) * time.Second)
	return
}

二、前端上传代码示例

<template>
    <div class="content">
            <a href="javascript:void(0)" @click="upload">
                上传
            </a>
        </div>
    </div>
</template>

<script>

import axios from 'axios'

export default {
        name: "putPreSign",
        methods: {
            upload: function(event) {
                // 获取预签名地址信息
                var url = "https://test.s3.cn-northwest-1.amazonaws.com.cn/xxx"
                // 预签名地址跨域处理
                url = url.replace("https://test.s3.cn-northwest-1.amazonaws.com.cn/","/test/")
                // 获取文件数据
                var file = event.target.files[0]
                // 组装请求头 / 主要看 X-Amz-SignedHeaders 参与预签名的参数有哪些, 另外Content-Length 会通过浏览器自动识别文件大小加入请求头
                header := {
                    headers: {
                        'Content-Type': this.file.type,
                        'X-Amz-Acl': 'public-read',
                        'X-Amz-Server-Side-Encryption': "AES256"
                    }
                }
                // 执行请求,注意,必须用 PUT 方式
                axios.put(url, file, header).then(res => {
                    console.log(res)
                }).catch( err => {
                    console.log(err)
                })
            }
        }
</script>

  vue3.0 下,修改根目录下的 vue.config.js 配置信息,添加跨域处理:

proxy: {
    '/test/*': {
        target: 'http://test.s3.cn-northwest-1.amazonaws.com.cn/',
        changeOrigin: true,
        pathRewrite: {
            '^/test': '/'
        }
    }
}

  常见错误:


  403: 验签失败,要检查 X-Amz-SignedHeaders 包含了哪些参数签名,对应检查请求头是否存在对应的信息;

  400:如果文件 MD5 参与验签,头部 content-md5 参数错误,可以不加入文件 MD5 参与预签名;

  404:bucket 存储桶不存在。

上传 HTTP 响应 200,则说明上传成功。

以上,祝好运!

猜你喜欢

转载自blog.csdn.net/createNo_1/article/details/114268110