使用golang的http.ResponseWriter进行AWS S3大文件反向代理

时间:2016-02-06 19:48:18

标签: amazon-web-services go amazon-s3

我有一个名为Download的请求处理程序,我想从Amazon S3访问一个大文件并将其推送到用户的浏览器。我的目标是:

  • 在授予用户访问文件之前记录一些请求信息
  • 不要将文件缓冲到内存中太多。文件可能会变得太大。

这是我到目前为止所探索的内容:

func Download(w http.ResponseWriter, r *http.Request) {

    sess := session.New(&aws.Config{
        Region:             aws.String("eu-west-1"),
        Endpoint:           aws.String("s3-eu-west-1.amazonaws.com"),
        S3ForcePathStyle:   aws.Bool(true),
        Credentials:        cred,
    })

    downloader := s3manager.NewDownloader(sess)
    // I can't write directly into the ResponseWriter. It doesn't implement WriteAt. 
    // Besides, it doesn't seem like the right thing to do.
    _, err := downloader.Download(w, &s3.GetObjectInput{
        Bucket: aws.String(BUCKET),
        Key: aws.String(filename),
    })
    if err != nil {
        log.Error(4, err.Error())
        return
    }

}

我想知道是否有更好的方法(考虑到我正在努力实现的目标)。

欢迎任何建议。提前谢谢: - )

2 个答案:

答案 0 :(得分:4)

如果文件可能很大,您不希望它通过您自己的服务器。 最好的方法(在我看来)是让用户直接从S3下载。

您可以通过生成 预先签名的网址 来执行此操作:

func Download(w http.ResponseWriter, r *http.Request) {

    ...

    sess := session.New(&aws.Config{
        Region:             aws.String("eu-west-1"),
        Endpoint:           aws.String("s3-eu-west-1.amazonaws.com"),
        S3ForcePathStyle:   aws.Bool(true),
        Credentials:        cred,
    })

    s3svc := s3.New(sess)
    req, _ := s3svc.GetObjectRequest(&s3.GetObjectInput{
        Bucket: aws.String(BUCKET),
        Key: aws.String(filename),
    })

    url, err := req.Presign(5 * time.Minute)
    if err != nil {
        //handle error
    }

    http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}

预签名网址仅在有限时间内有效(在此示例中为5分钟,根据您的需要调整)并将用户直接带到S3。无需再担心下载了!

答案 1 :(得分:1)

如果您 要做 要通过服务流式传输文件(而不是按照接受的答案中的建议直接下载)-

import (
    ...

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

func StreamDownloadHandler(w http.ResponseWriter, r *http.Request) {

    sess, awsSessErr := session.NewSession(&aws.Config{
        Region:      aws.String("eu-west-1"),
        Credentials: credentials.NewStaticCredentials("my-aws-id", "my-aws-secret", ""),
    })
    if awsSessErr != nil {
        http.Error(w, fmt.Sprintf("Error creating aws session %s", awsSessErr.Error()), http.StatusInternalServerError)
        return
    }

    result, err := s3.New(sess).GetObject(&s3.GetObjectInput{
        Bucket: aws.String("my-bucket"),
        Key:    aws.String("my-file-id"),
    })
    if err != nil {
        http.Error(w, fmt.Sprintf("Error getting file from s3 %s", err.Error()), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", "my-file.csv"))
    w.Header().Set("Cache-Control", "no-store")

    bytesWritten, copyErr := io.Copy(w, result.Body)
    if copyErr != nil {
        http.Error(w, fmt.Sprintf("Error copying file to the http response %s", copyErr.Error()), http.StatusInternalServerError)
        return
    }
    log.Printf("Download of \"%s\" complete. Wrote %s bytes", "my-file.csv", strconv.FormatInt(bytesWritten, 10))
}