mirror of
https://github.com/gmemstr/sliproad.git
synced 2024-09-20 00:21:15 +01:00
Simplify file serving from local or remote.
Prior, we would decide how to serve a file based on whether it was local or remote at a router level. This moves everything to an io.Copy call in the router with the Provider returning an io.Reader.
This commit is contained in:
parent
8127aaa6a8
commit
54fe5f9c5c
|
@ -7,7 +7,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -142,7 +144,7 @@ func (bp *BackblazeProvider) FilePath(path string) string {
|
||||||
return ""
|
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{}
|
client := &http.Client{}
|
||||||
// Get bucket name >:(
|
// Get bucket name >:(
|
||||||
bucketIdPayload := fmt.Sprintf(`{"accountId": "%s", "bucketId": "%s"}`, bp.Name, bp.Bucket)
|
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",
|
req, err := http.NewRequest("POST", bp.Location + "/b2api/v2/b2_list_buckets",
|
||||||
bytes.NewBuffer([]byte(bucketIdPayload)))
|
bytes.NewBuffer([]byte(bucketIdPayload)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
return nil, contenttype, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
req.Header.Add("Authorization", bp.Authentication)
|
req.Header.Add("Authorization", bp.Authentication)
|
||||||
|
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
return nil, contenttype, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
bucketData, err := ioutil.ReadAll(res.Body)
|
bucketData, err := ioutil.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
return nil, contenttype, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var data BackblazeBucketInfoPayload
|
var data BackblazeBucketInfoPayload
|
||||||
|
@ -175,22 +174,23 @@ func (bp *BackblazeProvider) RemoteFile(path string, w io.Writer) {
|
||||||
url,
|
url,
|
||||||
bytes.NewBuffer([]byte("")))
|
bytes.NewBuffer([]byte("")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
return nil, contenttype, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
req.Header.Add("Authorization", bp.Authentication)
|
req.Header.Add("Authorization", bp.Authentication)
|
||||||
file, err := client.Do(req)
|
file, err := client.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
return nil, contenttype, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.Copy(w, file.Body)
|
contenttype = mime.TypeByExtension(filepath.Ext(path))
|
||||||
if err != nil {
|
if contenttype == "" {
|
||||||
fmt.Println(err.Error())
|
var buf [512]byte
|
||||||
return
|
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 {
|
func (bp *BackblazeProvider) SaveFile(file io.Reader, filename string, path string) bool {
|
||||||
|
|
|
@ -4,7 +4,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"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}, "/")
|
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) {
|
contenttype = mime.TypeByExtension(filepath.Ext(rp))
|
||||||
return
|
|
||||||
|
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 {
|
func (dp *DiskProvider) SaveFile(file io.Reader, filename string, path string) bool {
|
||||||
|
|
|
@ -39,7 +39,7 @@ type FileProviderInterface interface {
|
||||||
Setup(args map[string]string) (ok bool)
|
Setup(args map[string]string) (ok bool)
|
||||||
GetDirectory(path string) (directory Directory)
|
GetDirectory(path string) (directory Directory)
|
||||||
FilePath(path string) (realpath string)
|
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)
|
SaveFile(file io.Reader, filename string, path string) (ok bool)
|
||||||
ObjectInfo(path string) (exists bool, isDir bool, location string)
|
ObjectInfo(path string) (exists bool, isDir bool, location string)
|
||||||
CreateDirectory(path string) (ok bool)
|
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.
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@ package router
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gmemstr/nas/files"
|
"io"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
"github.com/gmemstr/nas/files"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleProvider() handler {
|
func handleProvider() handler {
|
||||||
|
@ -29,7 +29,7 @@ func handleProvider() handler {
|
||||||
StatusCode: http.StatusInternalServerError,
|
StatusCode: http.StatusInternalServerError,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ok, isDir, location := provider.ObjectInfo(filename)
|
ok, isDir, _ := provider.ObjectInfo(filename)
|
||||||
if !ok {
|
if !ok {
|
||||||
return &httpError{
|
return &httpError{
|
||||||
Message: fmt.Sprintf("error locating file %s\n", filename),
|
Message: fmt.Sprintf("error locating file %s\n", filename),
|
||||||
|
@ -49,27 +49,25 @@ func handleProvider() handler {
|
||||||
}
|
}
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
return nil
|
return nil
|
||||||
}
|
} else {
|
||||||
|
stream, contenttype, err := provider.SendFile(filename, w)
|
||||||
|
|
||||||
// If the file is local, attempt to use http.ServeContent for correct headers.
|
if err != nil {
|
||||||
if location == files.FileIsLocal {
|
return &httpError{
|
||||||
rp := provider.FilePath(filename)
|
Message: fmt.Sprintf("error finding file %s\n", vars["file"]),
|
||||||
if rp != "" {
|
StatusCode: http.StatusNotFound,
|
||||||
f, err := os.Open(rp)
|
}
|
||||||
if err != nil {
|
}
|
||||||
return &httpError{
|
w.Header().Set("Content-Type", contenttype)
|
||||||
Message: fmt.Sprintf("error opening file %s\n", rp),
|
_, err = io.Copy(w, stream)
|
||||||
StatusCode: http.StatusInternalServerError,
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue