diff --git a/backends/localfs/localfs.go b/backends/localfs/localfs.go index 42e32b8..aaf487f 100644 --- a/backends/localfs/localfs.go +++ b/backends/localfs/localfs.go @@ -4,6 +4,7 @@ import ( "encoding/json" "io" "io/ioutil" + "net/http" "os" "path" "time" @@ -82,6 +83,18 @@ func (b LocalfsBackend) Get(key string) (metadata backends.Metadata, f io.ReadCl return } +func (b LocalfsBackend) ServeFile(key string, w http.ResponseWriter, r *http.Request) (err error) { + _, err = b.Head(key) + if err != nil { + return + } + + filePath := path.Join(b.filesPath, key) + http.ServeFile(w, r, filePath) + + return +} + func (b LocalfsBackend) writeMetadata(key string, metadata backends.Metadata) error { metaPath := path.Join(b.metaPath, key) diff --git a/backends/s3/s3.go b/backends/s3/s3.go index bfc6e1c..a558779 100644 --- a/backends/s3/s3.go +++ b/backends/s3/s3.go @@ -3,6 +3,7 @@ package s3 import ( "io" "io/ioutil" + "net/http" "os" "strconv" "time" @@ -79,6 +80,43 @@ func (b S3Backend) Get(key string) (metadata backends.Metadata, r io.ReadCloser, return } +func (b S3Backend) ServeFile(key string, w http.ResponseWriter, r *http.Request) (err error) { + var result *s3.GetObjectOutput + + if r.Header.Get("Range") != "" { + result, err = b.svc.GetObject(&s3.GetObjectInput{ + Bucket: aws.String(b.bucket), + Key: aws.String(key), + Range: aws.String(r.Header.Get("Range")), + }) + + w.WriteHeader(206) + w.Header().Set("Content-Range", *result.ContentRange) + w.Header().Set("Content-Length", strconv.FormatInt(*result.ContentLength, 10)) + w.Header().Set("Accept-Ranges", "bytes") + + } else { + result, err = b.svc.GetObject(&s3.GetObjectInput{ + Bucket: aws.String(b.bucket), + Key: aws.String(key), + }) + + } + + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if aerr.Code() == s3.ErrCodeNoSuchKey || aerr.Code() == "NotFound" { + err = backends.NotFoundErr + } + } + return + } + + _, err = io.Copy(w, result.Body) + + return +} + func mapMetadata(m backends.Metadata) map[string]*string { return map[string]*string{ "Expiry": aws.String(strconv.FormatInt(m.Expiry.Unix(), 10)), diff --git a/backends/storage.go b/backends/storage.go index 5d973c4..864d0a1 100644 --- a/backends/storage.go +++ b/backends/storage.go @@ -3,6 +3,7 @@ package backends import ( "errors" "io" + "net/http" "time" ) @@ -13,6 +14,7 @@ type StorageBackend interface { Get(key string) (Metadata, io.ReadCloser, error) Put(key string, r io.Reader, expiry time.Time, deleteKey, accessKey string) (Metadata, error) PutMetadata(key string, m Metadata) error + ServeFile(key string, w http.ResponseWriter, r *http.Request) error Size(key string) (int64, error) } diff --git a/fileserve.go b/fileserve.go index 27a28a9..90c1507 100644 --- a/fileserve.go +++ b/fileserve.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "io" "net/http" "net/url" "strconv" @@ -61,15 +60,11 @@ func fileServeHandler(c web.C, w http.ResponseWriter, r *http.Request) { } if r.Method != "HEAD" { - _, reader, err := storageBackend.Get(fileName) - if err != nil { - oopsHandler(c, w, r, RespAUTO, "Unable to open file.") - return - } - defer reader.Close() - if _, err = io.CopyN(w, reader, metadata.Size); err != nil { + storageBackend.ServeFile(fileName, w, r) + if err != nil { oopsHandler(c, w, r, RespAUTO, err.Error()) + return } } }