golang s3 文件上传
·
package main
import (
"bytes"
"context"
"flag"
"fmt"
"io"
"log"
"os"
"time"
"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"
)
// S3Client 封装 AWS S3 客户端,提供面向对象接口
type S3Client struct {
ctx context.Context
svc *s3.S3
logger *log.Logger
}
// NewS3Client 创建一个新的 S3Client 实例
func NewS3Client(ctx context.Context, endpoint, accessKey, secretKey, region string, disableSSL, forcePathStyle bool, logger *log.Logger) (*S3Client, error) {
conf := &aws.Config{
Endpoint: aws.String(endpoint),
S3ForcePathStyle: aws.Bool(forcePathStyle),
DisableSSL: aws.Bool(disableSSL),
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
Region: aws.String(region),
}
sess, err := session.NewSessionWithOptions(session.Options{Config: *conf})
if err != nil {
return nil, fmt.Errorf("创建 AWS session 失败: %v", err)
}
return &S3Client{
ctx: ctx,
svc: s3.New(sess),
logger: logger,
}, nil
}
// CreateBucket 创建存储桶
func (c *S3Client) CreateBucket(bucketName string) error {
input := &s3.CreateBucketInput{
Bucket: aws.String(bucketName),
}
_, err := c.svc.CreateBucket(input)
if err != nil {
return fmt.Errorf("创建存储桶失败: %v", err)
}
c.logger.Printf("存储桶 %s 创建成功", bucketName)
return nil
}
// ListBuckets 列出所有存储桶
func (c *S3Client) ListBuckets() error {
resp, err := c.svc.ListBuckets(&s3.ListBucketsInput{})
if err != nil {
return fmt.Errorf("列出存储桶失败: %v", err)
}
if len(resp.Buckets) == 0 {
fmt.Println("没有找到任何存储桶")
return nil
}
fmt.Println("存储桶列表:")
for _, b := range resp.Buckets {
fmt.Printf(" - %s (创建时间: %s)\n", aws.StringValue(b.Name), aws.TimeValue(b.CreationDate).Format(time.RFC3339))
}
return nil
}
// ListObjects 列出指定存储桶中的对象
func (c *S3Client) ListObjects(bucketName string) error {
input := &s3.ListObjectsInput{
Bucket: aws.String(bucketName),
}
resp, err := c.svc.ListObjects(input)
if err != nil {
return fmt.Errorf("列出对象失败: %v", err)
}
if len(resp.Contents) == 0 {
fmt.Printf("存储桶 %s 中没有对象\n", bucketName)
return nil
}
fmt.Printf("存储桶 %s 中的对象:\n", bucketName)
for _, obj := range resp.Contents {
fmt.Printf(" - %s (大小: %d bytes, 修改时间: %s)\n",
aws.StringValue(obj.Key), aws.Int64Value(obj.Size), aws.TimeValue(obj.LastModified).Format(time.RFC3339))
}
return nil
}
// PutObject 上传对象到存储桶
func (c *S3Client) PutObject(bucketName, objectName, filePath string) error {
data, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("读取文件失败: %v", err)
}
ctx, cancel := context.WithTimeout(c.ctx, 30*time.Second)
defer cancel()
_, err = c.svc.PutObjectWithContext(ctx, &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(objectName),
Body: bytes.NewReader(data),
})
if err != nil {
return fmt.Errorf("上传对象失败: %v", err)
}
c.logger.Printf("对象 %s 已上传到 %s", objectName, bucketName)
return nil
}
// GetObject 从存储桶下载对象
func (c *S3Client) GetObject(bucketName, objectName, outputFile string) error {
ctx, cancel := context.WithTimeout(c.ctx, 30*time.Second)
defer cancel()
out, err := c.svc.GetObjectWithContext(ctx, &s3.GetObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(objectName),
})
if err != nil {
return fmt.Errorf("下载对象失败: %v", err)
}
defer out.Body.Close()
data, err := io.ReadAll(out.Body)
if err != nil {
return fmt.Errorf("读取对象内容失败: %v", err)
}
if outputFile == "" {
fmt.Print(string(data))
} else {
if err := os.WriteFile(outputFile, data, 0644); err != nil {
return fmt.Errorf("写入文件失败: %v", err)
}
c.logger.Printf("对象 %s 已保存到 %s", objectName, outputFile)
}
return nil
}
// GetObjectAcl 获取对象的 ACL 并打印
func (c *S3Client) GetObjectAcl(bucketName, objectName string) error {
out, err := c.svc.GetObjectAcl(&s3.GetObjectAclInput{
Bucket: aws.String(bucketName),
Key: aws.String(objectName),
})
if err != nil {
return fmt.Errorf("获取对象 ACL 失败: %v", err)
}
fmt.Printf("对象 %s 的 ACL:\n", objectName)
fmt.Printf(" 所有者: %s\n", aws.StringValue(out.Owner.DisplayName))
for _, grant := range out.Grants {
grantee := grant.Grantee
permission := aws.StringValue(grant.Permission)
if grantee.DisplayName != nil {
fmt.Printf(" - 授权给 %s: %s\n", aws.StringValue(grantee.DisplayName), permission)
} else if grantee.URI != nil {
fmt.Printf(" - 授权给 %s: %s\n", aws.StringValue(grantee.URI), permission)
} else if grantee.ID != nil {
fmt.Printf(" - 授权给 %s: %s\n", aws.StringValue(grantee.ID), permission)
}
}
return nil
}
// 命令行参数结构
type config struct {
endpoint string
accessKey string
secretKey string
region string
disableSSL bool
forcePathStyle bool
}
func parseFlags() *config {
cfg := &config{}
flag.StringVar(&cfg.endpoint, "endpoint", getEnv("S3_ENDPOINT", ""), "S3 endpoint URL")
flag.StringVar(&cfg.accessKey, "access-key", getEnv("AWS_ACCESS_KEY_ID", ""), "AWS access key ID")
flag.StringVar(&cfg.secretKey, "secret-key", getEnv("AWS_SECRET_ACCESS_KEY", ""), "AWS secret access key")
flag.StringVar(&cfg.region, "region", getEnv("AWS_REGION", "cn"), "AWS region")
flag.BoolVar(&cfg.disableSSL, "disable-ssl", getEnvBool("S3_DISABLE_SSL", true), "disable SSL")
flag.BoolVar(&cfg.forcePathStyle, "force-path-style", getEnvBool("S3_FORCE_PATH_STYLE", false), "force path style")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "用法: %s [全局选项] <子命令> [子命令参数]\n", os.Args[0])
fmt.Fprintf(os.Stderr, "\n全局选项:\n")
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, "\n子命令:\n")
fmt.Fprintf(os.Stderr, " create-bucket <bucket> 创建存储桶\n")
fmt.Fprintf(os.Stderr, " list-buckets 列出所有存储桶\n")
fmt.Fprintf(os.Stderr, " list-objects <bucket> 列出存储桶中的对象\n")
fmt.Fprintf(os.Stderr, " put-object --bucket <bucket> --key <key> --file <path> 上传文件\n")
fmt.Fprintf(os.Stderr, " get-object --bucket <bucket> --key <key> [--output <file>] 下载对象(不指定输出则打印到 stdout)\n")
fmt.Fprintf(os.Stderr, " get-object-acl --bucket <bucket> --key <key> 获取对象 ACL\n")
fmt.Fprintf(os.Stderr, "\n环境变量:\n")
fmt.Fprintf(os.Stderr, " S3_ENDPOINT, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, S3_DISABLE_SSL, S3_FORCE_PATH_STYLE\n")
}
flag.Parse()
return cfg
}
func getEnv(key, defaultValue string) string {
if val := os.Getenv(key); val != "" {
return val
}
return defaultValue
}
func getEnvBool(key string, defaultValue bool) bool {
if val := os.Getenv(key); val != "" {
return val == "true" || val == "1"
}
return defaultValue
}
func main() {
cfg := parseFlags()
// 验证必需参数
if cfg.endpoint == "" {
log.Fatal("错误: 必须指定 endpoint (通过 -endpoint 或环境变量 S3_ENDPOINT)")
}
if cfg.accessKey == "" {
log.Fatal("错误: 必须指定 access-key (通过 -access-key 或环境变量 AWS_ACCESS_KEY_ID)")
}
if cfg.secretKey == "" {
log.Fatal("错误: 必须指定 secret-key (通过 -secret-key 或环境变量 AWS_SECRET_ACCESS_KEY)")
}
// 创建日志器和上下文
logger := log.New(os.Stderr, "[S3] ", log.LstdFlags)
ctx := context.Background()
// 实例化客户端
client, err := NewS3Client(ctx, cfg.endpoint, cfg.accessKey, cfg.secretKey, cfg.region, cfg.disableSSL, cfg.forcePathStyle, logger)
if err != nil {
log.Fatalf("初始化 S3 客户端失败: %v", err)
}
// 获取子命令
args := flag.Args()
if len(args) == 0 {
flag.Usage()
os.Exit(1)
}
cmd := args[0]
subArgs := args[1:]
switch cmd {
case "create-bucket":
if len(subArgs) < 1 {
log.Fatal("用法: create-bucket <bucket>")
}
if err := client.CreateBucket(subArgs[0]); err != nil {
log.Fatal(err)
}
case "list-buckets":
if err := client.ListBuckets(); err != nil {
log.Fatal(err)
}
case "list-objects":
if len(subArgs) < 1 {
log.Fatal("用法: list-objects <bucket>")
}
if err := client.ListObjects(subArgs[0]); err != nil {
log.Fatal(err)
}
case "put-object":
fs := flag.NewFlagSet("put-object", flag.ExitOnError)
bucket := fs.String("bucket", "", "存储桶名称")
key := fs.String("key", "", "对象键名")
file := fs.String("file", "", "要上传的文件路径")
fs.Parse(subArgs)
if *bucket == "" || *key == "" || *file == "" {
log.Fatal("put-object 需要 --bucket, --key, --file 参数")
}
if err := client.PutObject(*bucket, *key, *file); err != nil {
log.Fatal(err)
}
case "get-object":
fs := flag.NewFlagSet("get-object", flag.ExitOnError)
bucket := fs.String("bucket", "", "存储桶名称")
key := fs.String("key", "", "对象键名")
output := fs.String("output", "", "输出文件路径(可选)")
fs.Parse(subArgs)
if *bucket == "" || *key == "" {
log.Fatal("get-object 需要 --bucket 和 --key 参数")
}
if err := client.GetObject(*bucket, *key, *output); err != nil {
log.Fatal(err)
}
case "get-object-acl":
fs := flag.NewFlagSet("get-object-acl", flag.ExitOnError)
bucket := fs.String("bucket", "", "存储桶名称")
key := fs.String("key", "", "对象键名")
fs.Parse(subArgs)
if *bucket == "" || *key == "" {
log.Fatal("get-object-acl 需要 --bucket 和 --key 参数")
}
if err := client.GetObjectAcl(*bucket, *key); err != nil {
log.Fatal(err)
}
default:
log.Fatalf("未知子命令: %s\n", cmd)
flag.Usage()
os.Exit(1)
}
}
更多推荐

所有评论(0)