aws有两种账户类型MFA 和IAM
MFA 根用户
IAM 由根用户衍生的权限受控用户
IAM链接:https://us-east-1.console.aws.amazon.com/iamv2/home#/home
这里有几个坑 :
1: 不明白aws的endpoint 具体指什么,没有看到官文有哪里告知了这个 endpoint是啥,(试了也不是接入点)这个url是自己试出来的。
2: 首先要去开一个IAM用户,然后要配置accessKey和secretKey ,和 权限策略才可以使用。安全凭证(accessKey和secretKey )在IAM里设置步骤:
https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/users
这里有个坑就是 就算你设置好IAM用户,也给IAM用户生成了访问密钥,但是在代码里执行的时候还是AccessDenied. 原因是要给IAM用户配置访问权限(IAM用户策略中缺乏在s3上执行操作的权限),参考:https://aws.amazon.com/cn/premiumsupport/knowledge-center/athena-access-denied-status-code-403/
https://repost.aws/zh-Hans/knowledge-center/s3-troubleshoot-403
在策略中创建策略然后附加到IAM用户
或者在用户的权限那里添加内联策略。
直接使用可视化编辑器去控制策略。json格式的因为不熟就不要自己写了。在这里还可以限制IAM的访问ip,可以将它限制为公司服务器的公网ip和自己本地调试时候的公网ip。
这个策略可以以后在 用户-权限-权限策略-策略名称- (点进去) 那里更新。
这是使用IAM用户,其实也可以直接使用MFA根用户创建访问密钥。根用户的密钥可以直接使用,拥有所有权限,不需要配置权限策略。
添加MFA
创建访问密钥
可以看到并不建议这么做,
所以我们使用IAM就好。
下面上代码,代码如下:
package service
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"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"mime/multipart"
"os"
"stargate_backend/utils"
"strconv"
)
/*
这里是前端传到后端文件,使用的是*multipart.FileHeader格式。不是直接在本地用os.open()打开。
*/
var (
// IAM账户 需要在IAM添加合适的访问策略 才有s3服务的访问权限
accessKey = "xxxx"
secretKey = "xxxxxxx"
//根用户 MFA 非最佳实践 但具有根权限 无需配置权限 可以直接使用
//accessKey = "xxxx"
//secretKey = "xx/xxx"
region = "ap-northeast-1"
endpoint = "https://s3.ap-northeast-1.amazonaws.com/"
svc *s3.S3
)
func AWSInit() error {
//只要不修改session,session就可以安全的并发使用。
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
Endpoint: aws.String(endpoint),
Region: aws.String(region),
//minio:true,oss:false
S3ForcePathStyle: aws.Bool(false),
//SDK 支持使用客户端 TLS 证书配置的环境和会话选项,这些证书作为客户端 TLS 握手的一部分发送以进行客户端身份验证。
//如果使用,则需要 Cert 和 Key 值。如果缺少一个,或者无法加载文件的内容,则会返回一个错误。
//ClientTLSCert: nil,
//ClientTLSKey: nil,
})
if err != nil {
panic(err)
}
svc = s3.New(sess)
return err
}
//S3 上传管理器确定文件是否可以拆分为更小的部分并并行上传。您可以自定义并行上传的数量和上传部分的大小。
func AWSUploadWithClient(file *multipart.FileHeader, svc *s3.S3, dir string) (string, error) {
uploader := s3manager.NewUploaderWithClient(svc, func(u *s3manager.Uploader) {
//定义将在内存中缓冲25个MiB的策略
//u.BufferProvider = s3manager.NewBufferedReadSeekerWriteToPool(25 * 1024 * 1024)
//指定要上传的每个部分的缓冲区大小(以字节为单位)。每个部分的最小大小为 5 MB。 DefaultUploadPartSize
u.PartSize = 64 * 1024 * 1024 // 每个部分 64MB
// 指定要并行上传的part数量。 默认为5
u.Concurrency = s3manager.DefaultUploadConcurrency
})
open, err := file.Open() // 可以通过一个*multipart.FileHeader 的open() 方法的获取一个multipart.File文件 它实现了io.Reader接口
if err != nil {
return "", err
}
id, _ := utils.GenID()
var xxx = "你的桶名字"
uploadOutput, err := uploader.Upload(&s3manager.UploadInput{
Body: open, // io.Reader类型
Bucket: aws.String(xxx), // 指定要上传的bucket
Key: aws.String(dir + "/" + strconv.Itoa(id)), // 如果bucket下边有文件夹,那么通过在key前加上指定路径,来达到上传到指定文件夹的效果。
ACL: aws.String("public-read"), // 指定ACL权限。一般上传图片类的都是期望可以在前端通过url获取资源的,为public-read。具体可以参考 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/acl-overview.html#CannedACL
})
if err != nil {
if multierr, ok := err.(s3manager.MultiUploadFailure); ok {
// Process error and its associated uploadID
fmt.Println("Error:", multierr.Code(), multierr.Message(), multierr.UploadID())
} else {
// Process error generically
fmt.Println("Error:", err.Error())
}
return "", err
}
url := "https://xxx.s3.ap-northeast-1.amazonaws.com/" + dir + "/" + strconv.Itoa(id)
fmt.Printf("UploadID:%s\n", uploadOutput.UploadID)
fmt.Printf("ETag:%s\n", *uploadOutput.ETag)
fmt.Printf("Location:%s\n", uploadOutput.Location)
return url, err
}
//https://docs.aws.amazon.com/zh_cn/sdk-for-go/v1/developer-guide/sdk-utilities.html
//传输管理器 上传和下载管理器可以分解大型对象,以便可以将它们分成多个部分并行传输。这使得恢复中断的传输变得容易。
//S3 下载管理器确定文件是否可以拆分为更小的部分并并行下载。您可以自定义并行下载的数量和下载部分的大小。
func DownloadWithClient(svc *s3.S3, bucket, key string) {
download := s3manager.NewDownloaderWithClient(svc, func(d *s3manager.Downloader) {
d.PartSize = 64 * 1024 * 1024 // 64MB per part
//d.BufferProvider = s3manager.NewPooledBufferedWriterReadFromProvider(25 * 1024 * 1024)
})
f, err := os.Create("d_" + key)
if err != nil {
panic(err)
}
defer f.Close()
n, err := download.Download(f, &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
})
if err != nil {
panic(err)
}
fmt.Printf("Download.n:%d\n", n)
}