initial v0.8 pre-release
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"image"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -116,6 +117,39 @@ func (s *System) Upload(content []byte, fileKey string) error {
|
||||
return w.Close()
|
||||
}
|
||||
|
||||
// UploadMultipart upload the provided multipart file to the fileKey location.
|
||||
func (s *System) UploadMultipart(fh *multipart.FileHeader, fileKey string) error {
|
||||
f, err := fh.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
mt, err := mimetype.DetectReader(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rewind
|
||||
f.Seek(0, io.SeekStart)
|
||||
|
||||
opts := &blob.WriterOptions{
|
||||
ContentType: mt.String(),
|
||||
}
|
||||
|
||||
w, err := s.bucket.NewWriter(s.ctx, fileKey, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.ReadFrom(f); err != nil {
|
||||
w.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return w.Close()
|
||||
}
|
||||
|
||||
// Delete deletes stored file at fileKey location.
|
||||
func (s *System) Delete(fileKey string) error {
|
||||
return s.bucket.Delete(s.ctx, fileKey)
|
||||
@@ -233,7 +267,7 @@ func (s *System) Serve(response http.ResponseWriter, fileKey string, name string
|
||||
response.Header().Set("Content-Length", strconv.FormatInt(r.Size(), 10))
|
||||
response.Header().Set("Content-Security-Policy", "default-src 'none'; media-src 'self'; style-src 'unsafe-inline'; sandbox")
|
||||
|
||||
// All HTTP date/time stamps MUST be represented in Greenwich Mean Time (GMT)
|
||||
// all HTTP date/time stamps MUST be represented in Greenwich Mean Time (GMT)
|
||||
// (see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1)
|
||||
//
|
||||
// NB! time.LoadLocation may fail on non-Unix systems (see https://github.com/pocketbase/pocketbase/issues/45)
|
||||
@@ -242,6 +276,13 @@ func (s *System) Serve(response http.ResponseWriter, fileKey string, name string
|
||||
response.Header().Set("Last-Modified", r.ModTime().In(location).Format("Mon, 02 Jan 06 15:04:05 MST"))
|
||||
}
|
||||
|
||||
// set a default cache-control header
|
||||
// (valid for 30 days but the cache is allowed to reuse the file for any requests
|
||||
// that are made in the last day while revalidating the response in the background)
|
||||
if response.Header().Get("Cache-Control") == "" {
|
||||
response.Header().Set("Cache-Control", "max-age=2592000, stale-while-revalidate=86400")
|
||||
}
|
||||
|
||||
// copy from the read range to response.
|
||||
_, err := io.Copy(response, r)
|
||||
|
||||
@@ -282,6 +323,7 @@ func (s *System) CreateThumb(originalKey string, thumbKey, thumbSize string) err
|
||||
defer r.Close()
|
||||
|
||||
// create imaging object from the original reader
|
||||
// (note: only the first frame for animated image formats)
|
||||
img, decodeErr := imaging.Decode(r, imaging.AutoOrientation(true))
|
||||
if decodeErr != nil {
|
||||
return decodeErr
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package filesystem_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/png"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -128,6 +131,46 @@ func TestFileSystemDeletePrefix(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSystemUploadMultipart(t *testing.T) {
|
||||
dir := createTestDir(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// create multipart form file
|
||||
body := new(bytes.Buffer)
|
||||
mp := multipart.NewWriter(body)
|
||||
w, err := mp.CreateFormFile("test", "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating form file: %v", err)
|
||||
}
|
||||
w.Write([]byte("demo"))
|
||||
mp.Close()
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/", body)
|
||||
req.Header.Add("Content-Type", mp.FormDataContentType())
|
||||
|
||||
file, fh, err := req.FormFile("test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to fetch form file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
// ---
|
||||
|
||||
fs, err := filesystem.NewLocal(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fs.Close()
|
||||
|
||||
uploadErr := fs.UploadMultipart(fh, "newdir/newkey.txt")
|
||||
if uploadErr != nil {
|
||||
t.Fatal(uploadErr)
|
||||
}
|
||||
|
||||
if exists, _ := fs.Exists("newdir/newkey.txt"); !exists {
|
||||
t.Fatalf("Expected newdir/newkey.txt to exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSystemUpload(t *testing.T) {
|
||||
dir := createTestDir(t)
|
||||
defer os.RemoveAll(dir)
|
||||
@@ -232,6 +275,10 @@ func TestFileSystemServe(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
if scenario.expectError {
|
||||
continue
|
||||
}
|
||||
|
||||
result := r.Result()
|
||||
|
||||
for hName, hValue := range scenario.expectHeaders {
|
||||
@@ -244,6 +291,10 @@ func TestFileSystemServe(t *testing.T) {
|
||||
if v := result.Header.Get("X-Frame-Options"); v != "" {
|
||||
t.Errorf("(%s) Expected the X-Frame-Options header to be unset, got %v", scenario.path, v)
|
||||
}
|
||||
|
||||
if v := result.Header.Get("Cache-Control"); v == "" {
|
||||
t.Errorf("(%s) Expected Cache-Control header to be set, got empty string", scenario.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user