From 550e722a533ffe66a07af8491172a6a80ab012db Mon Sep 17 00:00:00 2001 From: Gabriel Simmer Date: Sat, 29 May 2021 10:48:32 +0100 Subject: [PATCH] Implement SendFile() and ObjectInfo() for S3. Also removed a now useless parameter for file providers, which used to be for writing directly to the HTTP response. --- files/backblaze.go | 2 +- files/disk.go | 2 +- files/fileprovider.go | 4 ++-- files/s3.go | 48 ++++++++++++++++++++++++++++++++++++++++--- router/filerouter.go | 2 +- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/files/backblaze.go b/files/backblaze.go index 95b2f52..d1d2e21 100644 --- a/files/backblaze.go +++ b/files/backblaze.go @@ -140,7 +140,7 @@ func (bp *BackblazeProvider) GetDirectory(path string) Directory { return finalDir } -func (bp *BackblazeProvider) SendFile(path string, w io.Writer) (stream io.Reader, contenttype string, err error) { +func (bp *BackblazeProvider) SendFile(path string) (stream io.Reader, contenttype string, err error) { client := &http.Client{} // Get bucket name >:( bucketIdPayload := fmt.Sprintf(`{"accountId": "%s", "bucketId": "%s"}`, bp.Name, bp.Bucket) diff --git a/files/disk.go b/files/disk.go index 2549fca..a0c4b52 100644 --- a/files/disk.go +++ b/files/disk.go @@ -45,7 +45,7 @@ func (dp *DiskProvider) GetDirectory(path string) Directory { } } -func (dp *DiskProvider) SendFile(path string, writer io.Writer) (stream io.Reader, contenttype string, err error) { +func (dp *DiskProvider) SendFile(path string) (stream io.Reader, contenttype string, err error) { rp := strings.Join([]string{dp.Location,path}, "/") f, err := os.Open(rp) if err != nil { diff --git a/files/fileprovider.go b/files/fileprovider.go index f7df85c..2073417 100644 --- a/files/fileprovider.go +++ b/files/fileprovider.go @@ -38,7 +38,7 @@ type FileInfo struct { type FileProviderInterface interface { Setup(args map[string]string) (ok bool) GetDirectory(path string) (directory Directory) - SendFile(path string, writer io.Writer) (stream io.Reader, contenttype string, err error) + SendFile(path string) (stream io.Reader, contenttype string, err error) SaveFile(file io.Reader, filename string, path string) (ok bool) ObjectInfo(path string) (exists bool, isDir bool, location string) CreateDirectory(path string) (ok bool) @@ -58,7 +58,7 @@ func (f FileProvider) GetDirectory(path string) Directory { } // RemoteFile will bypass http.ServeContent() and instead write directly to the response. -func (f FileProvider) SendFile(path string, writer io.Writer) (stream io.Reader, contenttype string, err error) { +func (f FileProvider) SendFile(path string) (stream io.Reader, contenttype string, err error) { return } diff --git a/files/s3.go b/files/s3.go index 83db84d..77b9e80 100644 --- a/files/s3.go +++ b/files/s3.go @@ -3,6 +3,10 @@ package files import ( "fmt" "io" + "mime" + "path/filepath" + "net/http" + // I _really_ don't want to deal with AWS API stuff by hand. "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" @@ -10,6 +14,7 @@ import ( ) var svc s3.S3 +var sess *session.Session type S3Provider struct { FileProvider @@ -40,6 +45,15 @@ func (s *S3Provider) GetDirectory(path string) Directory { dir := Directory{} for _, item := range resp.Contents { + ik := *item.Key + // Why is this here? AWS returns a complete list of files, including + // files within subdirectories (prefixed with the dir name). So we can + // ignore directories altogether -- I would prefer to display them but + // not sure what the best method of distinguishing them in ObjectInfo() + // would be. + if ik[len(ik)-1:] == "/" { + continue + } file := FileInfo{ IsDirectory: false, Name: *item.Key, @@ -51,8 +65,23 @@ func (s *S3Provider) GetDirectory(path string) Directory { } // RemoteFile will bypass http.ServeContent() and instead write directly to the response. -func (s *S3Provider) SendFile(path string, writer io.Writer) (stream io.Reader, contenttype string, err error) { - return +func (s *S3Provider) SendFile(path string) (stream io.Reader, contenttype string, err error) { + req, err := svc.GetObject(&s3.GetObjectInput{ + Bucket: &s.Bucket, + Key: &path, + }) + if err != nil { + return stream, contenttype, err + } + + contenttype = mime.TypeByExtension(filepath.Ext(path)) + if contenttype == "" { + var buf [512]byte + n, _ := io.ReadFull(req.Body, buf[:]) + contenttype = http.DetectContentType(buf[:n]) + } + + return req.Body, contenttype, err } // SaveFile will save a file with the contents of the io.Reader at the path specified. @@ -64,7 +93,20 @@ func (s *S3Provider) SaveFile(file io.Reader, filename string, path string) bool // Should return whether the path exists, if the path is a directory, and if it lives on disk. // (see constants defined: `FILE_IS_REMOTE` and `FILE_IS_LOCAL`) func (s *S3Provider) ObjectInfo(path string) (bool, bool, string) { - return true, true, "" + if path == "" { + return true, true, "" + } + + _, err := svc.GetObject(&s3.GetObjectInput{ + Bucket: &s.Bucket, + Key: &path, + }) + if err != nil { + fmt.Println(err) + return false, false, "" + } + + return true, false, "" } // CreateDirectory will create a directory on services that support it. diff --git a/router/filerouter.go b/router/filerouter.go index 6de343e..7b38c37 100644 --- a/router/filerouter.go +++ b/router/filerouter.go @@ -50,7 +50,7 @@ func handleProvider() handler { w.Write(data) return nil } else { - stream, contenttype, err := provider.SendFile(filename, w) + stream, contenttype, err := provider.SendFile(filename) if err != nil { return &httpError{