最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

S3命令行工具Docker容器運(yùn)行過程

 更新時(shí)間:2026年04月27日 08:38:04   作者:赴前塵  
本文介紹了使用Dockerfile和兩種方式(環(huán)境變量文件和逐個(gè)傳遞環(huán)境變量)來運(yùn)行容器并構(gòu)建鏡像,以實(shí)現(xiàn)從S3存儲(chǔ)桶上傳和下載文件的功能

源代碼

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 客戶端,提供面向?qū)ο蠼涌?
type S3Client struct {
	ctx    context.Context
	svc    *s3.S3
	logger *log.Logger
}

// NewS3Client 創(chuàng)建一個(gè)新的 S3Client 實(shí)例
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("創(chuàng)建 AWS session 失敗: %v", err)
	}

	return &S3Client{
		ctx:    ctx,
		svc:    s3.New(sess),
		logger: logger,
	}, nil
}

// CreateBucket 創(chuàng)建存儲(chǔ)桶
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("創(chuàng)建存儲(chǔ)桶失敗: %v", err)
	}
	c.logger.Printf("存儲(chǔ)桶 %s 創(chuàng)建成功", bucketName)
	return nil
}

// ListBuckets 列出所有存儲(chǔ)桶
func (c *S3Client) ListBuckets() error {
	resp, err := c.svc.ListBuckets(&s3.ListBucketsInput{})
	if err != nil {
		return fmt.Errorf("列出存儲(chǔ)桶失敗: %v", err)
	}
	if len(resp.Buckets) == 0 {
		fmt.Println("沒有找到任何存儲(chǔ)桶")
		return nil
	}
	fmt.Println("存儲(chǔ)桶列表:")
	for _, b := range resp.Buckets {
		fmt.Printf("  - %s (創(chuàng)建時(shí)間: %s)\n", aws.StringValue(b.Name), aws.TimeValue(b.CreationDate).Format(time.RFC3339))
	}
	return nil
}

// ListObjects 列出指定存儲(chǔ)桶中的對(duì)象
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("列出對(duì)象失敗: %v", err)
	}
	if len(resp.Contents) == 0 {
		fmt.Printf("存儲(chǔ)桶 %s 中沒有對(duì)象\n", bucketName)
		return nil
	}
	fmt.Printf("存儲(chǔ)桶 %s 中的對(duì)象:\n", bucketName)
	for _, obj := range resp.Contents {
		fmt.Printf("  - %s (大小: %d bytes, 修改時(shí)間: %s)\n",
			aws.StringValue(obj.Key), aws.Int64Value(obj.Size), aws.TimeValue(obj.LastModified).Format(time.RFC3339))
	}
	return nil
}

// PutObject 上傳對(duì)象到存儲(chǔ)桶
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),
		ACL:    aws.String("public-read"),
	})
	if err != nil {
		return fmt.Errorf("上傳對(duì)象失敗: %v", err)
	}
	c.logger.Printf("對(duì)象 %s 已上傳到 %s", objectName, bucketName)
	return nil
}

// GetObject 從存儲(chǔ)桶下載對(duì)象
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("下載對(duì)象失敗: %v", err)
	}
	defer out.Body.Close()

	data, err := io.ReadAll(out.Body)
	if err != nil {
		return fmt.Errorf("讀取對(duì)象內(nèi)容失敗: %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("對(duì)象 %s 已保存到 %s", objectName, outputFile)
	}
	return nil
}

// GetObjectAcl 獲取對(duì)象的 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("獲取對(duì)象 ACL 失敗: %v", err)
	}
	fmt.Printf("對(duì)象 %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("  - 授權(quán)給 %s: %s\n", aws.StringValue(grantee.DisplayName), permission)
		} else if grantee.URI != nil {
			fmt.Printf("  - 授權(quán)給 %s: %s\n", aws.StringValue(grantee.URI), permission)
		} else if grantee.ID != nil {
			fmt.Printf("  - 授權(quán)給 %s: %s\n", aws.StringValue(grantee.ID), permission)
		}
	}
	return nil
}

// 命令行參數(shù)結(jié)構(gòu)
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 [全局選項(xiàng)] <子命令> [子命令參數(shù)]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\n全局選項(xiàng):\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\n子命令:\n")
		fmt.Fprintf(os.Stderr, "  create-bucket <bucket>           創(chuàng)建存儲(chǔ)桶\n")
		fmt.Fprintf(os.Stderr, "  list-buckets                      列出所有存儲(chǔ)桶\n")
		fmt.Fprintf(os.Stderr, "  list-objects <bucket>             列出存儲(chǔ)桶中的對(duì)象\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>] 下載對(duì)象(不指定輸出則打印到 stdout)\n")
		fmt.Fprintf(os.Stderr, "  get-object-acl --bucket <bucket> --key <key>             獲取對(duì)象 ACL\n")
		fmt.Fprintf(os.Stderr, "\n環(huá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() {
	// S3_ENDPOINT="https://..." AWS_ACCESS_KEY_ID="..." AWS_SECRET_ACCESS_KEY="..." go run main.go put-object --bucket my-bucket --key myfile.jpg --file ./photo.jpg
	cfg := parseFlags()

	// 驗(yàn)證必需參數(shù)
	if cfg.endpoint == "" {
		log.Fatal("錯(cuò)誤: 必須指定 endpoint (通過 -endpoint 或環(huán)境變量 S3_ENDPOINT)")
	}
	if cfg.accessKey == "" {
		log.Fatal("錯(cuò)誤: 必須指定 access-key (通過 -access-key 或環(huán)境變量 AWS_ACCESS_KEY_ID)")
	}
	if cfg.secretKey == "" {
		log.Fatal("錯(cuò)誤: 必須指定 secret-key (通過 -secret-key 或環(huán)境變量 AWS_SECRET_ACCESS_KEY)")
	}

	// 創(chuàng)建日志器和上下文
	logger := log.New(os.Stderr, "[S3] ", log.LstdFlags)
	ctx := context.Background()

	// 實(shí)例化客戶端
	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", "", "存儲(chǔ)桶名稱")
		key := fs.String("key", "", "對(duì)象鍵名")
		file := fs.String("file", "", "要上傳的文件路徑")
		fs.Parse(subArgs)
		if *bucket == "" || *key == "" || *file == "" {
			log.Fatal("put-object 需要 --bucket, --key, --file 參數(shù)")
		}
		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", "", "存儲(chǔ)桶名稱")
		key := fs.String("key", "", "對(duì)象鍵名")
		output := fs.String("output", "", "輸出文件路徑(可選)")
		fs.Parse(subArgs)
		if *bucket == "" || *key == "" {
			log.Fatal("get-object 需要 --bucket 和 --key 參數(shù)")
		}
		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", "", "存儲(chǔ)桶名稱")
		key := fs.String("key", "", "對(duì)象鍵名")
		fs.Parse(subArgs)
		if *bucket == "" || *key == "" {
			log.Fatal("get-object-acl 需要 --bucket 和 --key 參數(shù)")
		}
		if err := client.GetObjectAcl(*bucket, *key); err != nil {
			log.Fatal(err)
		}

	default:
		log.Fatalf("未知子命令: %s\n", cmd)
		flag.Usage()
		os.Exit(1)
	}
}

Dockerfile

# 使用多階段構(gòu)建 (Multi-stage build) 以減小最終鏡像體積

# 第一階段:構(gòu)建階段
FROM golang:1.20-alpine AS builder

# 設(shè)置 Go 環(huán)境變量:?jiǎn)⒂?Go Modules 并使用國內(nèi)代理
ENV GO111MODULE=on \
    GOPROXY=https://goproxy.cn,direct

# (可選)更換 Alpine 軟件源為國內(nèi)鏡像,加速后續(xù)可能出現(xiàn)的 apk 操作
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

# 設(shè)置工作目錄
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

# 復(fù)制源代碼
COPY . .

# 構(gòu)建可執(zhí)行文件,關(guān)閉 CGO 以生成靜態(tài)鏈接的二進(jìn)制文件
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o s3-tool main.go

# 第二階段:運(yùn)行階段
FROM alpine:latest

# 更換 Alpine 軟件源為國內(nèi)鏡像(中科大源)
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

# 安裝 CA 證書,以便在 HTTPS 請(qǐng)求中驗(yàn)證 SSL 證書
RUN apk --no-cache add ca-certificates

# 設(shè)置工作目錄
WORKDIR /root/

# 從構(gòu)建階段復(fù)制可執(zhí)行文件
COPY --from=builder /app/s3-tool .

# 設(shè)置容器啟動(dòng)時(shí)的默認(rèn)命令,顯示幫助信息
ENTRYPOINT ["./s3-tool"]

容器方式運(yùn)行

構(gòu)建鏡像

docker build -t s3-tool:latest .

方法一:使用環(huán)境變量文件(推薦)

創(chuàng)建一個(gè) s3.env 文件:

S3_ENDPOINT=https://your-s3-endpoint.com
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1
S3_DISABLE_SSL=false
S3_FORCE_PATH_STYLE=false
  • 列出所有存儲(chǔ)桶
docker run --rm --env-file s3.env s3-tool:latest list-buckets 
  • 創(chuàng)建新存儲(chǔ)桶
docker run --rm --env-file s3.env s3-tool:latest create-bucket my-new-bucket 
  • 上傳文件(需要掛載宿主機(jī)文件)
docker run --rm \
  --env-file s3.env \
  -v /path/to/your/local/file:/tmp/upload-file \
  s3-tool:latest put-object --bucket my-bucket --key remote-key --file /tmp/upload-file 

說明:使用 -v 將宿主機(jī)文件掛載到容器內(nèi)可訪問的路徑。

  • 下載對(duì)象到宿主機(jī)
docker run --rm \
  --env-file s3.env \
  -v /path/to/save/on/host:/tmp/download \
  s3-tool:latest get-object --bucket my-bucket --key remote-key --output /tmp/download/local-file.txt 

說明:下載的文件會(huì)出現(xiàn)在宿主機(jī)的 /path/to/save/on/host/local-file.txt。

方法二:逐個(gè)傳遞環(huán)境變量

  • 列出所有存儲(chǔ)桶
docker run --rm \
  -e S3_ENDPOINT="https://your-s3-endpoint.com" \
  -e AWS_ACCESS_KEY_ID="your-access-key" \
  -e AWS_SECRET_ACCESS_KEY="your-secret-key" \
  -e AWS_REGION="us-east-1" \
  s3-tool:latest list-buckets

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解決Docker無法拉取鏡像(需配置鏡像加速器)

    解決Docker無法拉取鏡像(需配置鏡像加速器)

    在Linux中拉取Docker鏡像失敗可能因無法連接DockerHub,解決方法包括配置國內(nèi)鏡像加速器和重啟Docker服務(wù),以提升速度并確保配置生效
    2025-09-09
  • 服務(wù)器上使用 Docker 的常用命令清單完整流程

    服務(wù)器上使用 Docker 的常用命令清單完整流程

    本文介紹了在Ubuntu服務(wù)器上使用Docker的常用命令,涵蓋了鏡像管理、容器操作、日志查看、網(wǎng)絡(luò)與端口映射、清理資源等方面,適合日常開發(fā)和運(yùn)維使用,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • Docker安裝和使用kkfileview的步驟

    Docker安裝和使用kkfileview的步驟

    本文主要介紹了Docker安裝和使用kkfileview的步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • Docker部署Minio集群的實(shí)現(xiàn)步驟

    Docker部署Minio集群的實(shí)現(xiàn)步驟

    本文介紹 Docker Compose 部署 Minio 集群的方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-05-05
  • Docker容器 日志中文亂碼問題解決辦法

    Docker容器 日志中文亂碼問題解決辦法

    這篇文章主要介紹了Docker容器 日志中文亂碼問題解決辦法的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • Docker 部署Go的兩種基礎(chǔ)鏡像的實(shí)現(xiàn)

    Docker 部署Go的兩種基礎(chǔ)鏡像的實(shí)現(xiàn)

    這篇文章主要介紹了Docker 部署Go的兩種基礎(chǔ)鏡像的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • 解決docker鏡像(centos系統(tǒng))中無sudo命令問題

    解決docker鏡像(centos系統(tǒng))中無sudo命令問題

    這篇文章主要介紹了解決docker鏡像(centos系統(tǒng))中無sudo命令問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,
    2023-11-11
  • 使用Docker搭建MySQL數(shù)據(jù)庫服務(wù)的方法詳解

    使用Docker搭建MySQL數(shù)據(jù)庫服務(wù)的方法詳解

    在現(xiàn)代應(yīng)用程序開發(fā)中,使用數(shù)據(jù)庫是必不可少的,文將介紹如何使用Docker搭建MySQL數(shù)據(jù)庫服務(wù),讓你在本地環(huán)境中快速部署一個(gè)MySQL實(shí)例,感興趣的可以了解下
    2024-03-03
  • docker報(bào)錯(cuò)Container is not running問題及解決

    docker報(bào)錯(cuò)Container is not running問題及解決

    這篇文章主要介紹了docker報(bào)錯(cuò)Container is not running問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Docker學(xué)習(xí)筆記之Docker部署Java web系統(tǒng)

    Docker學(xué)習(xí)筆記之Docker部署Java web系統(tǒng)

    這篇文章主要介紹了Docker學(xué)習(xí)筆記之Docker部署Java web系統(tǒng),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02

最新評(píng)論

兴隆县| 密山市| 城步| 彭阳县| 大连市| 蒙山县| 潞西市| 化隆| 唐海县| 寿光市| 东平县| 长武县| 新干县| 青河县| 厦门市| 千阳县| 邹城市| 五大连池市| 合水县| 祥云县| 海伦市| 清远市| 阿合奇县| 临桂县| 三明市| 安乡县| 武山县| 芮城县| 丘北县| 灵石县| 鲁甸县| 昆明市| 陇南市| 河西区| 宁城县| 海门市| 靖江市| 松溪县| 天台县| 抚州市| 盐亭县|