2014-12-25 1 views
1

Я использую пакет Goamz и могу использовать некоторую помощь, получая bucket.Multi, чтобы передать ответ HTTP GET на S3.Golang multipart загружает с chunked `http.GET` и Goamz` multi.PutAll`

Я загружу файл 2+ GB через chunked HTTP, и я хотел бы передать его прямо в ведро S3.

Оказалось, что мне нужно обернуть resp.Body с чем-то, так что я могу передать реализацию s3.ReaderAtSeeker в multi.PutAll

// set up s3 
auth, _ := aws.EnvAuth() 
s3Con := s3.New(auth, aws.USEast) 
bucket := s3Con.Bucket("bucket-name") 

// make http request to URL 
resp, err := http.Get(export_url) 
if err != nil { 
    fmt.Printf("Get error %v\n", err) 
    return 
} 

defer resp.Body.Close() 

// set up multi-part 
multi, err := bucket.InitMulti(s3Path, "text/plain", s3.Private, s3.Options{}) 
if err != nil { 
    fmt.Printf("InitMulti error %v\n", err) 
    return 
} 

// Need struct that implements: s3.ReaderAtSeeker 
// type ReaderAtSeeker interface { 
// io.ReaderAt 
// io.ReadSeeker 
// } 

rs := // Question: what can i wrap `resp.Body` in? 

parts, err := multi.PutAll(rs, 5120) 
if err != nil { 
    fmt.Printf("PutAll error %v\n", err) 
    return 
} 

err = multi.Complete(parts) 
if err != nil { 
    fmt.Printf("Complete error %v\n", err) 
    return 
} 

В настоящее время я получаю следующее (ожидаемое) сообщение об ошибке при попытке запустить мою программу:

./main.go:50: cannot use resp.Body (type io.ReadCloser) as type s3.ReaderAtSeeker in argument to multi.PutAll: 
    io.ReadCloser does not implement s3.ReaderAtSeeker (missing ReadAt method) 

ответ

1

вы не указали, какой пакет вы используете для доступа к API S3, но я предполагаю, что это за одинhttps://github.com/mitchellh/goamz/.

Поскольку ваш файл имеет значительный размер, возможным решением может быть использование multi.PutPart. Это даст вам больше контроля, чем multi.PutAll. Использование Reader из стандартной библиотеки, ваш подход будет:

  1. Получить Content-Length из заголовка ответа
  2. Получить количество необходимых деталей на основе Content-Length и partSize
  3. Loop над номером части и прочитать [] байт из response.Body в bytes.Reader и вызвать multi.PutPart
  4. Получите части от multi.ListParts
  5. вызова multi.Complete с частями.

У меня нет доступа к S3, поэтому я не могу проверить свою гипотезу, но вышеупомянутое может стоить изучить, если вы еще этого не сделали.

0

Более простой подход заключается в использовании - http://github.com/minio/minio-go

Он реализует PutObject(), который является полностью управляемой самодостаточными операции для загрузки больших файлов. Он также автоматически выполняет многопроцессорную обработку данных объемом более 5 МБ параллельно. если не указана предопределенная ContentLength. Он будет продолжать загружать, пока не достигнет EOF.

В следующем примере показано, как это сделать, если у вас нет предопределенной длины ввода, но есть io.Reader, который передает потоки. В этом примере я использовал «os.Stdin» в качестве эквивалента для вашего помеченного ввода.

package main 

import (
    "log" 
    "os" 

    "github.com/minio/minio-go" 
) 

func main() { 
    config := minio.Config{ 
     AccessKeyID:  "YOUR-ACCESS-KEY-HERE", 
     SecretAccessKey: "YOUR-PASSWORD-HERE", 
     Endpoint:  "https://s3.amazonaws.com", 
    } 
    s3Client, err := minio.New(config) 
    if err != nil { 
     log.Fatalln(err) 
    } 

    err = s3Client.PutObject("mybucket", "myobject", "application/octet-stream", 0, os.Stdin) 
    if err != nil { 
     log.Fatalln(err) 
    } 

} 
$ echo "Hello my new-object" | go run stream-object.go