Extract GetReader into own interface

This commit is contained in:
Marius 2016-01-19 22:32:15 +01:00
parent 29047eddc2
commit c9207d8c5f
3 changed files with 36 additions and 11 deletions

View File

@ -43,14 +43,6 @@ type DataStore interface {
// requests. It may return an os.ErrNotExist which will be interpreted as a // requests. It may return an os.ErrNotExist which will be interpreted as a
// 404 Not Found. // 404 Not Found.
GetInfo(id string) (FileInfo, error) GetInfo(id string) (FileInfo, error)
// Get an io.Reader to allow downloading the file. This feature is not
// part of the official tus specification. If this additional function
// should not be enabled any call to GetReader should return
// tusd.ErrNotImplemented. The length of the resource is determined by
// retrieving the offset using GetInfo.
// If the returned reader also implements the io.Closer interface, the
// Close() method will be invoked once everything has been read.
GetReader(id string) (io.Reader, error)
} }
// TerminaterDataStore is the interface which must be implemented by DataStores // TerminaterDataStore is the interface which must be implemented by DataStores
@ -97,3 +89,21 @@ type LockerDataStore interface {
// UnlockUpload releases an existing lock for the given upload. // UnlockUpload releases an existing lock for the given upload.
UnlockUpload(id string) error UnlockUpload(id string) error
} }
// GetReaderDataStore is the interface which must be implemented if handler should
// expose and support the GET route. It will allow clients to download the
// content of an upload regardless whether it's finished or not.
// Please, be aware that this feature is not part of the official tus
// specification. Instead it's a custom mechanism by tusd.
type GetReaderDataStore interface {
DataStore
// GetReader returns a reader which allows iterating of the content of an
// upload specified by its ID. It should attempt to provide a reader even if
// the upload has not been finished yet but it's not required.
// If the returned reader also implements the io.Closer interface, the
// Close() method will be invoked once everything has been read.
// If the given upload could not be found, the error tusd.ErrNotFound should
// be returned.
GetReader(id string) (io.Reader, error)
}

View File

@ -38,7 +38,6 @@ func NewHandler(config Config) (*Handler, error) {
mux.Post("", http.HandlerFunc(handler.PostFile)) mux.Post("", http.HandlerFunc(handler.PostFile))
mux.Head(":id", http.HandlerFunc(handler.HeadFile)) mux.Head(":id", http.HandlerFunc(handler.HeadFile))
mux.Get(":id", http.HandlerFunc(handler.GetFile))
mux.Add("PATCH", ":id", http.HandlerFunc(handler.PatchFile)) mux.Add("PATCH", ":id", http.HandlerFunc(handler.PatchFile))
// Only attach the DELETE handler if the Terminate() method is provided // Only attach the DELETE handler if the Terminate() method is provided
@ -46,6 +45,11 @@ func NewHandler(config Config) (*Handler, error) {
mux.Del(":id", http.HandlerFunc(handler.DelFile)) mux.Del(":id", http.HandlerFunc(handler.DelFile))
} }
// GET handler requires the GetReader() method
if _, ok := config.DataStore.(GetReaderDataStore); ok {
mux.Get(":id", http.HandlerFunc(handler.GetFile))
}
return routedHandler, nil return routedHandler, nil
} }

View File

@ -402,6 +402,12 @@ func (handler *UnroutedHandler) PatchFile(w http.ResponseWriter, r *http.Request
// GetFile handles requests to download a file using a GET request. This is not // GetFile handles requests to download a file using a GET request. This is not
// part of the specification. // part of the specification.
func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request) { func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request) {
dataStore, ok := handler.dataStore.(GetReaderDataStore)
if !ok {
handler.sendError(w, r, ErrNotImplemented)
return
}
id, err := extractIDFromPath(r.URL.Path) id, err := extractIDFromPath(r.URL.Path)
if err != nil { if err != nil {
handler.sendError(w, r, err) handler.sendError(w, r, err)
@ -430,7 +436,7 @@ func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request)
} }
// Get reader // Get reader
src, err := handler.dataStore.GetReader(id) src, err := dataStore.GetReader(id)
if err != nil { if err != nil {
handler.sendError(w, r, err) handler.sendError(w, r, err)
return return
@ -545,10 +551,15 @@ func (handler *UnroutedHandler) sizeOfUploads(ids []string) (size int64, err err
// Fill an empty upload with the content of the uploads by their ids. The data // Fill an empty upload with the content of the uploads by their ids. The data
// will be written in the order as they appear in the slice // will be written in the order as they appear in the slice
func (handler *UnroutedHandler) fillFinalUpload(id string, uploads []string) error { func (handler *UnroutedHandler) fillFinalUpload(id string, uploads []string) error {
dataStore, ok := handler.dataStore.(GetReaderDataStore)
if !ok {
return ErrNotImplemented
}
readers := make([]io.Reader, len(uploads)) readers := make([]io.Reader, len(uploads))
for index, uploadID := range uploads { for index, uploadID := range uploads {
reader, err := handler.dataStore.GetReader(uploadID) reader, err := dataStore.GetReader(uploadID)
if err != nil { if err != nil {
return err return err
} }