Initial PUT implementation
This commit is contained in:
parent
62b35dde82
commit
07ac9a43f5
|
@ -3,6 +3,8 @@ package main
|
||||||
// This is very simple for now and will be enhanced as needed.
|
// This is very simple for now and will be enhanced as needed.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
@ -43,3 +45,27 @@ func initFile(fileId string, size int64, contentType string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func putFileChunk(fileId string, start int64, end int64, r io.Reader) error {
|
||||||
|
d := dataPath(fileId)
|
||||||
|
file, err := os.OpenFile(d, os.O_WRONLY, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if n, err := file.Seek(start, os.SEEK_SET); err != nil {
|
||||||
|
return err
|
||||||
|
} else if n != start {
|
||||||
|
return errors.New("putFileChunk: seek failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
size := end - start + 1
|
||||||
|
if n, err := io.CopyN(file, r, size); err != nil {
|
||||||
|
return err
|
||||||
|
} else if n != size {
|
||||||
|
return errors.New("putFileChunk: partial copy")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -24,8 +24,9 @@ func route(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Server", "tusd")
|
w.Header().Set("Server", "tusd")
|
||||||
|
|
||||||
if r.Method == "POST" && r.URL.Path == "/files" {
|
if r.Method == "POST" && r.URL.Path == "/files" {
|
||||||
createFile(w, r)
|
postFiles(w, r)
|
||||||
} else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil {
|
} else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil {
|
||||||
|
id := match[1]
|
||||||
// WIP
|
// WIP
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case "HEAD":
|
case "HEAD":
|
||||||
|
@ -33,7 +34,7 @@ func route(w http.ResponseWriter, r *http.Request) {
|
||||||
case "GET":
|
case "GET":
|
||||||
reply(w, http.StatusNotImplemented, "File download")
|
reply(w, http.StatusNotImplemented, "File download")
|
||||||
case "PUT":
|
case "PUT":
|
||||||
reply(w, http.StatusOK, "chunk created")
|
putFile(w, r, id)
|
||||||
default:
|
default:
|
||||||
reply(w, http.StatusMethodNotAllowed, "Invalid http method")
|
reply(w, http.StatusMethodNotAllowed, "Invalid http method")
|
||||||
}
|
}
|
||||||
|
@ -47,7 +48,7 @@ func reply(w http.ResponseWriter, code int, message string) {
|
||||||
fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message)
|
fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFile(w http.ResponseWriter, r *http.Request) {
|
func postFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
|
contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reply(w, http.StatusBadRequest, err.Error())
|
reply(w, http.StatusBadRequest, err.Error())
|
||||||
|
@ -75,6 +76,27 @@ func createFile(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @TODO: Return X-Resume header
|
||||||
|
|
||||||
w.Header().Set("Location", "/files/"+id)
|
w.Header().Set("Location", "/files/"+id)
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
|
||||||
|
contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
|
||||||
|
if err != nil {
|
||||||
|
reply(w, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO: Check that file exists
|
||||||
|
// @TODO: Make sure contentRange.Size matches file size
|
||||||
|
|
||||||
|
if err := putFileChunk(fileId, contentRange.Start, contentRange.End, r.Body); err != nil {
|
||||||
|
// @TODO: Could be a 404 as well
|
||||||
|
reply(w, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO: Return X-Resume header
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue