慕斯709654
根据文档,DeleteBucket状态,必须先删除存储桶中的所有对象(包括所有对象版本和删除标记),然后才能删除存储桶本身。现在,要从启用版本控制的存储桶中删除版本,我们可以使用DeleteObject,其中指出,要删除特定版本,您必须是存储桶所有者并且必须使用版本 ID 子资源。使用此子资源会永久删除该版本。使用DeleteObjects,类似地指出,在 XML 中,如果要从启用版本控制的存储桶中删除对象的特定版本,您可以提供对象键名称和可选的版本 ID。在使用以下命令(先决条件 - Docker、Docker Compose、AWS CLI)创建存储桶并使用包含版本的文件填充它之后,我整理了一个示例程序,并针对LocalStack进行了测试。curl -O https://raw.githubusercontent.com/localstack/localstack/master/docker-compose.ymlexport SERVICES="s3"docker-compose upexport AWS_ACCESS_KEY_ID="test"export AWS_SECRET_ACCESS_KEY="test"export AWS_DEFAULT_REGION="us-east-1"aws --endpoint-url=http://localhost:4566 s3 mb s3://testbucketaws --endpoint-url=http://localhost:4566 s3api put-bucket-versioning --bucket testbucket --versioning-configuration Status=Enabledfor i in 1 2 3; do aws --endpoint-url=http://localhost:4566 s3 cp main.go s3://testbucket/main.go aws --endpoint-url=http://localhost:4566 s3 cp go.mod s3://testbucket/go.moddoneaws --endpoint-url=http://localhost:4566 s3api list-object-versions --bucket testbucket运行前设置以下环境变量export AWS_ENDPOINT="http://localhost:4566"export S3_BUCKET="testbucket"package mainimport ( "context" "fmt" "log" "os" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types")type s3Client struct { *s3.Client}func main() { awsEndpoint := os.Getenv("AWS_ENDPOINT") bucketName := os.Getenv("S3_BUCKET") cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithEndpointResolverWithOptions(aws.EndpointResolverWithOptionsFunc( func(service, region string, options ...interface{}) (aws.Endpoint, error) { return aws.Endpoint{ URL: awsEndpoint, HostnameImmutable: true, }, nil })), ) if err != nil { log.Fatalf("Cannot load the AWS configs: %s", err) } serviceClient := s3.NewFromConfig(cfg) client := &s3Client{ Client: serviceClient, } fmt.Printf(">>> Bucket: %s\n", bucketName) objects, err := client.listObjects(bucketName) if err != nil { log.Fatal(err) } if len(objects) > 0 { fmt.Printf(">>> List objects in the bucket: \n") for _, object := range objects { fmt.Printf("%s\n", object) } } else { fmt.Printf(">>> No objects in the bucket.\n") } if client.versioningEnabled(bucketName) { fmt.Printf(">>> Versioning is enabled.\n") objectVersions, err := client.listObjectVersions(bucketName) if err != nil { log.Fatal(err) } if len(objectVersions) > 0 { fmt.Printf(">>> List objects with versions: \n") for key, versions := range objectVersions { fmt.Printf("%s: ", key) for _, version := range versions { fmt.Printf("\n\t%s ", version) } fmt.Println() } } if len(objectVersions) > 0 { fmt.Printf(">>> Delete objects with versions.\n") if err := client.deleteObjects(bucketName, objectVersions); err != nil { log.Fatal(err) } objectVersions, err = client.listObjectVersions(bucketName) if err != nil { log.Fatal(err) } if len(objectVersions) > 0 { fmt.Printf(">>> List objects with versions after deletion: \n") for key, version := range objectVersions { fmt.Printf("%s: %s\n", key, version) } } else { fmt.Printf(">>> No objects in the bucket after deletion.\n") } } } fmt.Printf(">>> Delete the bucket.\n") if err := client.deleteBucket(bucketName); err != nil { log.Fatal(err) }}func (c *s3Client) versioningEnabled(bucket string) bool { output, err := c.GetBucketVersioning(context.TODO(), &s3.GetBucketVersioningInput{ Bucket: aws.String(bucket), }) if err != nil { return false } return output.Status == "Enabled"}func (c *s3Client) listObjects(bucket string) ([]string, error) { var objects []string output, err := c.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{ Bucket: aws.String(bucket), }) if err != nil { return nil, err } for _, object := range output.Contents { objects = append(objects, aws.ToString(object.Key)) } return objects, nil}func (c *s3Client) listObjectVersions(bucket string) (map[string][]string, error) { var objectVersions = make(map[string][]string) output, err := c.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{ Bucket: aws.String(bucket), }) if err != nil { return nil, err } for _, object := range output.Versions { if _, ok := objectVersions[aws.ToString(object.Key)]; ok { objectVersions[aws.ToString(object.Key)] = append(objectVersions[aws.ToString(object.Key)], aws.ToString(object.VersionId)) } else { objectVersions[aws.ToString(object.Key)] = []string{aws.ToString(object.VersionId)} } } return objectVersions, err}func (c *s3Client) deleteObjects(bucket string, objectVersions map[string][]string) error { var identifiers []types.ObjectIdentifier for key, versions := range objectVersions { for _, version := range versions { identifiers = append(identifiers, types.ObjectIdentifier{ Key: aws.String(key), VersionId: aws.String(version), }) } } _, err := c.DeleteObjects(context.TODO(), &s3.DeleteObjectsInput{ Bucket: aws.String(bucket), Delete: &types.Delete{ Objects: identifiers, }, }) if err != nil { return err } return nil}func (c *s3Client) deleteBucket(bucket string) error { _, err := c.DeleteBucket(context.TODO(), &s3.DeleteBucketInput{ Bucket: aws.String(bucket), }) if err != nil { return err } return nil}请注意,在listObjectVersions方法中,我将VersionIds 与Keys 映射。 for _, object := range output.Versions { if _, ok := objectVersions[aws.ToString(object.Key)]; ok { objectVersions[aws.ToString(object.Key)] = append(objectVersions[aws.ToString(object.Key)], aws.ToString(object.VersionId)) } else { objectVersions[aws.ToString(object.Key)] = []string{aws.ToString(object.VersionId)} } }然后在deleteObjects方法中,当传递ObjectIdentifiers 时,我为一个对象的所有版本传递了 sKey和ObjectIds。 for key, versions := range objectVersions { for _, version := range versions { identifiers = append(identifiers, types.ObjectIdentifier{ Key: aws.String(key), VersionId: aws.String(version), }) } }