diff --git a/files/backblaze.go b/files/backblaze.go index 881ffb2..5495911 100644 --- a/files/backblaze.go +++ b/files/backblaze.go @@ -7,7 +7,9 @@ import ( "fmt" "io" "io/ioutil" + "mime" "net/http" + "path/filepath" "strings" ) @@ -142,7 +144,7 @@ func (bp *BackblazeProvider) FilePath(path string) string { return "" } -func (bp *BackblazeProvider) RemoteFile(path string, w io.Writer) { +func (bp *BackblazeProvider) SendFile(path string, w io.Writer) (stream io.Reader, contenttype string, err error) { client := &http.Client{} // Get bucket name >:( bucketIdPayload := fmt.Sprintf(`{"accountId": "%s", "bucketId": "%s"}`, bp.Name, bp.Bucket) @@ -150,20 +152,17 @@ func (bp *BackblazeProvider) RemoteFile(path string, w io.Writer) { req, err := http.NewRequest("POST", bp.Location + "/b2api/v2/b2_list_buckets", bytes.NewBuffer([]byte(bucketIdPayload))) if err != nil { - fmt.Println(err.Error()) - return + return nil, contenttype, err } req.Header.Add("Authorization", bp.Authentication) res, err := client.Do(req) if err != nil { - fmt.Println(err.Error()) - return + return nil, contenttype, err } bucketData, err := ioutil.ReadAll(res.Body) if err != nil { - fmt.Println(err.Error()) - return + return nil, contenttype, err } var data BackblazeBucketInfoPayload @@ -175,22 +174,23 @@ func (bp *BackblazeProvider) RemoteFile(path string, w io.Writer) { url, bytes.NewBuffer([]byte(""))) if err != nil { - fmt.Println(err.Error()) - return + return nil, contenttype, err } req.Header.Add("Authorization", bp.Authentication) file, err := client.Do(req) if err != nil { - fmt.Println(err.Error()) - return + return nil, contenttype, err } - _, err = io.Copy(w, file.Body) - if err != nil { - fmt.Println(err.Error()) - return + contenttype = mime.TypeByExtension(filepath.Ext(path)) + if contenttype == "" { + var buf [512]byte + n, _ := io.ReadFull(file.Body, buf[:]) + contenttype = http.DetectContentType(buf[:n]) } + + return file.Body, contenttype, err } func (bp *BackblazeProvider) SaveFile(file io.Reader, filename string, path string) bool { diff --git a/files/disk.go b/files/disk.go index 3b3e048..1c04c67 100644 --- a/files/disk.go +++ b/files/disk.go @@ -4,7 +4,10 @@ import ( "fmt" "io" "io/ioutil" + "mime" + "net/http" "os" + "path/filepath" "strings" ) @@ -42,13 +45,22 @@ func (dp *DiskProvider) GetDirectory(path string) Directory { } } -func (dp *DiskProvider) FilePath(path string) string { +func (dp *DiskProvider) SendFile(path string, writer io.Writer) (stream io.Reader, contenttype string, err error) { rp := strings.Join([]string{dp.Location,path}, "/") - return rp -} + f, err := os.Open(rp) + if err != nil { + return stream, contenttype, err + } -func (dp *DiskProvider) RemoteFile(path string, writer io.Writer) { - return + contenttype = mime.TypeByExtension(filepath.Ext(rp)) + + if contenttype == "" { + var buf [512]byte + n, _ := io.ReadFull(f, buf[:]) + contenttype = http.DetectContentType(buf[:n]) + } + + return f, contenttype, nil } func (dp *DiskProvider) SaveFile(file io.Reader, filename string, path string) bool { diff --git a/files/fileprovider.go b/files/fileprovider.go index 94aea3d..ebf3a61 100644 --- a/files/fileprovider.go +++ b/files/fileprovider.go @@ -39,7 +39,7 @@ type FileProviderInterface interface { Setup(args map[string]string) (ok bool) GetDirectory(path string) (directory Directory) FilePath(path string) (realpath string) - RemoteFile(path string, writer io.Writer) + SendFile(path string, writer io.Writer) (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) @@ -64,7 +64,7 @@ func (f FileProvider) FilePath(path string) string { } // RemoteFile will bypass http.ServeContent() and instead write directly to the response. -func (f FileProvider) RemoteFile(path string, writer io.Writer) { +func (f FileProvider) SendFile(path string, writer io.Writer) (stream io.Reader, contenttype string, err error) { return } diff --git a/router/filerouter.go b/router/filerouter.go index f127f47..6de343e 100644 --- a/router/filerouter.go +++ b/router/filerouter.go @@ -3,14 +3,14 @@ package router import ( "encoding/json" "fmt" - "github.com/gmemstr/nas/files" - "github.com/gorilla/mux" + "io" "net/http" "net/url" - "os" "sort" "strings" - "time" + + "github.com/gmemstr/nas/files" + "github.com/gorilla/mux" ) func handleProvider() handler { @@ -29,7 +29,7 @@ func handleProvider() handler { StatusCode: http.StatusInternalServerError, } } - ok, isDir, location := provider.ObjectInfo(filename) + ok, isDir, _ := provider.ObjectInfo(filename) if !ok { return &httpError{ Message: fmt.Sprintf("error locating file %s\n", filename), @@ -49,27 +49,25 @@ func handleProvider() handler { } w.Write(data) return nil - } + } else { + stream, contenttype, err := provider.SendFile(filename, w) - // If the file is local, attempt to use http.ServeContent for correct headers. - if location == files.FileIsLocal { - rp := provider.FilePath(filename) - if rp != "" { - f, err := os.Open(rp) - if err != nil { - return &httpError{ - Message: fmt.Sprintf("error opening file %s\n", rp), - StatusCode: http.StatusInternalServerError, - } + if err != nil { + return &httpError{ + Message: fmt.Sprintf("error finding file %s\n", vars["file"]), + StatusCode: http.StatusNotFound, + } + } + w.Header().Set("Content-Type", contenttype) + _, err = io.Copy(w, stream) + if err != nil { + return &httpError{ + Message: fmt.Sprintf("unable to write %s\n", vars["file"]), + StatusCode: http.StatusBadGateway, } - http.ServeContent(w, r, filename, time.Time{}, f) } } - // If the file is remote, then delegate the writing to the response to the provider. - // This isn't a great workaround, but avoids caching the whole file in mem or on disk. - if location == files.FileIsRemote { - provider.RemoteFile(filename, w) - } + return nil }