1

我需要使用GetObjectGo AWS SDK 中的一系列函数从 S3 存储桶中获取对象,其中对象的键可能以一个或多个斜杠开头。但是,SDK 似乎删除了那些前导斜杠,从而更改了密钥。

我创建了存储桶并将一些数据放入如下:

$ aws s3 mb <TEST BUCKET>
$ aws s3 cp <SOME FILE> s3://<TEST BUCKET>//leadingslash

以下代码显示ListObjects正确返回带有前导斜杠的密钥,但是当运行日志显示GET请求在没有前导斜杠的情况下完成。

package main

import (
    "log"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
)

func main() {
    bucket := "<TEST BUCKET>"
    region := "<TEST BUCKET REGION>"
    config := (&aws.Config{Region: &region}).WithLogLevel(aws.LogDebugWithHTTPBody)
    s3svc := s3.New(session.New(config))

    listInput := s3.ListObjectsInput{
        Bucket: &bucket,
    }
    listOutput, err := s3svc.ListObjects(&listInput)
    if err != nil {
        log.Fatalf("Failed to list objects: %v", err)
    } else {
        log.Printf("Good: %v", listOutput)
    }

    for _, object := range listOutput.Contents {
        getInput := s3.GetObjectInput{
            Bucket: &bucket,
            Key:    object.Key,
        }
        getOutput, err := s3svc.GetObject(&getInput)
        if err != nil {
            log.Fatalf("Failed to HEAD object: %v", err)
        } else {
            log.Printf("Good: %v", getOutput)
        }
    }
}

启用调试日志记录的调用GetObject表明 SDK 执行以下请求:

GET /leadingslash HTTP/1.1

这缺少前导斜杠并返回 404 错误。

我应该如何使用 Go SDK 获取此类对象?我无法控制对象的键。

我已经尝试在将密钥传递给之前对密钥进行 URL 转义,GetObject但是百分号被转义并且密钥发生了变化。

我使用 Go 1.9 linux/amd64 和 SDK 1.12.62。

4

2 回答 2

1

刚遇到同样的问题,但经过一段时间的努力和调试后,我找到了另一个解决方案。

解决方案是将其添加到您的 s3 客户端配置中。

DisableRestProtocolURICleaning: aws.Bool(true),

来自AWS SDK for Go API 参考

自动 URI 清理

与键包含相邻斜杠的对象交互(例如,bucketname/foo//bar/objectname)需要在服务客户端使用的 aws.Config 结构中将 DisableRestProtocolURICleaning 设置为 true。

svc := s3.New(sess, &aws.Config{
    DisableRestProtocolURICleaning: aws.Bool(true),
})
out, err := svc.GetObject(&s3.GetObjectInput {
    Bucket: aws.String("bucketname"),
        Key: aws.String("//foo//bar//moo"),
})
于 2019-10-25T21:47:03.573 回答
1

按照@michael-sqlbot 的建议,这是通过使用自定义逻辑来构建请求来解决的:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "time"

    "github.com/aws/aws-sdk-go/aws/credentials"
    "github.com/aws/aws-sdk-go/aws/signer/v4"
    "github.com/aws/aws-sdk-go/private/protocol/rest"
)

const (
    BUCKET = "<TEST BUCKET>"
    KEY    = "/leadingslashkey"
    REGION = "<TEST BUCKET REGION>"
)

func main() {
    credentials := credentials.NewEnvCredentials()
    signer := v4.NewSigner(credentials)
    request, err := http.NewRequest(
        http.MethodGet,
        fmt.Sprintf("https://%s.s3.%s.amazonaws.com/%s", BUCKET, REGION, rest.EscapePath(KEY, false)),
        nil,
    )
    if err != nil {
        println(err.Error())
        return
    }
    header, err := signer.Sign(
        request,
        nil,
        "s3",
        REGION,
        time.Now(),
    )
    if err != nil {
        println(err, err.Error())
        return
    }
    fmt.Printf("%#v\n", header)
    fmt.Printf("%#v\n", request)
    client := http.Client{}
    response, err := client.Do(request)
    if err != nil {
        println(err, err.Error())
        return
    }
    fmt.Printf("%#v\n", response)
    out, _ := ioutil.ReadAll(response.Body)
    println(string(out))
}
于 2018-01-24T16:27:07.643 回答