如何在键具有前导斜杠时如何使用GetObject

时间:2018-01-16 16:06:03

标签: amazon-s3 aws-sdk-go

我需要使用Go AWS SDK中的GetObject系列函数从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获取此类对象?我无法控制对象的密钥。

在将密钥传递给GetObject之前,我已经尝试过网址转义,但百分号会被转义并且密钥会发生变化。

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

2 个答案:

答案 0 :(得分: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))
}

答案 1 :(得分:1)

只是遇到了同样的问题,但经过一段时间的调试和调试后,我发现了另一个解决方案。

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

DisableRestProtocolURICleaning: aws.Bool(true),

来自AWS SDK for Go API Reference

  

自动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"),
})