func moveFile(db *postgres.DB, repo *data.FileRepo, f data.BackendFile, delete bool) error { b := repo.DefaultBackend() tx, err := db.Begin() if err != nil { return err } stream, err := f.Backend.Open(tx, f.FileInfo, false) if err != nil { tx.Rollback() return err } if err := b.Put(tx, f.FileInfo, stream, false); err != nil { tx.Rollback() return err } if err := repo.SetBackend(tx, f.ID, b.Name()); err != nil { tx.Rollback() return err } if delete { if err := f.Backend.Delete(tx, f.FileInfo); err != nil { // print but don't return error if deletion of old file fails, we don't want to lose files log.Printf("Error deleting %s (%s) from %s: %s", f.FileInfo.Name, f.ID, f.Backend.Name(), err) } } return tx.Commit() }
func testDelete(r *data.FileRepo, t *testing.T) { put := func(path string) { if err := r.Put(path, bytes.NewReader([]byte("data")), 0, "text/plain"); err != nil { t.Fatal(err) } } del := func(path string) { if err := r.Delete(path); err != nil { t.Fatal(err) } } assertExists := func(path string) { if _, err := r.Get(path, false); err != nil { t.Fatal(err) } } assertNotExists := func(path string) { if _, err := r.Get(path, false); err != backend.ErrNotFound { t.Fatalf("expected path %q to not exist, got err=%v", path, err) } } put("/dir/foo") put("/dir/foo.txt") put("/dir/bar.txt") del("/dir/foo") assertNotExists("/dir/foo") assertExists("/dir/foo.txt") assertExists("/dir/bar.txt") del("/dir") assertNotExists("/dir/foo") assertNotExists("/dir/foo.txt") assertNotExists("/dir/bar.txt") }
func testExternalBackendReplace(r *data.FileRepo, t *testing.T) { put := func(path string) { if err := r.Put(path, bytes.NewReader([]byte("data")), 0, "text/plain"); err != nil { t.Fatal(err) } } get := func(path string) string { file, err := r.Get("/replace.txt", true) if err != nil { t.Fatal(err) } redirect, ok := file.FileStream.(backend.Redirector) if !ok { t.Fatal("file stream is not a redirector") } url := redirect.RedirectURL() res, err := http.Get(url) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 200 { t.Fatalf("expected status 200, got %d", res.StatusCode) } return url } put("/replace.txt") firstURL := get("/replace.txt") put("/replace.txt") res, err := http.Get(firstURL) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode == 200 { t.Fatal("unexpected status 200") } get("/replace.txt") }
func handler(r *data.FileRepo) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { path := path.Clean(req.URL.Path) if path == "/" { if req.Method == "HEAD" { return } else if req.Method != "GET" { w.WriteHeader(404) return } paths, err := r.List(req.URL.Query().Get("dir")) if err != nil && err != backend.ErrNotFound { errorResponse(w, err) return } if paths == nil { paths = []string{} } sort.Strings(paths) w.WriteHeader(200) json.NewEncoder(w).Encode(paths) return } switch req.Method { case "HEAD", "GET": file, err := r.Get(path, req.Method == "GET") if err != nil { errorResponse(w, err) return } if file.FileStream != nil { defer file.Close() } if r, ok := file.FileStream.(backend.Redirector); ok && req.Method == "GET" { http.Redirect(w, req, r.RedirectURL(), http.StatusFound) return } w.Header().Set("Content-Length", strconv.FormatInt(file.Size, 10)) w.Header().Set("Content-Type", file.Type) w.Header().Set("Etag", file.ETag) http.ServeContent(w, req, path, file.ModTime, file) case "PUT": var err error if src := req.Header.Get("Blobstore-Copy-From"); src != "" { err = r.Copy(path, src) } else { var offset int64 if s := req.Header.Get("Blobstore-Offset"); s != "" { offset, err = strconv.ParseInt(s, 10, 64) if err != nil { errorResponse(w, err) return } } err = r.Put(path, req.Body, offset, req.Header.Get("Content-Type")) } if err != nil { errorResponse(w, err) return } w.WriteHeader(200) case "DELETE": err := r.Delete(path) if err != nil { errorResponse(w, err) return } w.WriteHeader(200) default: w.WriteHeader(http.StatusMethodNotAllowed) } }) }