[#6792] added filesystem.System.GetReuploadableFile method
This commit is contained in:
parent
7ffe9f63a5
commit
e80d64414b
|
@ -5,12 +5,15 @@
|
||||||
- Updated `app.DB()` to automatically routes raw write SQL statements to the nonconcurrent db pool ([#6689](https://github.com/pocketbase/pocketbase/discussions/6689)).
|
- Updated `app.DB()` to automatically routes raw write SQL statements to the nonconcurrent db pool ([#6689](https://github.com/pocketbase/pocketbase/discussions/6689)).
|
||||||
_For the rare cases when it is needed users still have the option to explicitly target the specific pool they want using `app.ConcurrentDB()`/`app.NonconcurrentDB()`._
|
_For the rare cases when it is needed users still have the option to explicitly target the specific pool they want using `app.ConcurrentDB()`/`app.NonconcurrentDB()`._
|
||||||
|
|
||||||
- ⚠️ Soft-deprecated and replaced `fsys.GetFile(fileKey)` with `fsys.GetReader(fileKey)` to avoid the confusion with `filesystem.File`.
|
|
||||||
_The old method will still continue to work for at least until v0.29.0 but you'll get a console warning to replace it with `GetReader`._
|
|
||||||
|
|
||||||
- ⚠️ Changed the default `json` field max size to 1MB.
|
- ⚠️ Changed the default `json` field max size to 1MB.
|
||||||
_Users still have the option to adjust the default limit from the collection field options but keep in mind that storing large strings/blobs in the database is known to cause performance issues and should be avoided when possible._
|
_Users still have the option to adjust the default limit from the collection field options but keep in mind that storing large strings/blobs in the database is known to cause performance issues and should be avoided when possible._
|
||||||
|
|
||||||
|
- ⚠️ Soft-deprecated and replaced `filesystem.System.GetFile(fileKey)` with `filesystem.System.GetReader(fileKey)` to avoid the confusion with `filesystem.File`.
|
||||||
|
_The old method will still continue to work for at least until v0.29.0 but you'll get a console warning to replace it with `GetReader`._
|
||||||
|
|
||||||
|
- Added new `filesystem.System.GetReuploadableFile(fileKey, preserveName)` method to return an existing blob as a `*filesystem.File` value ([#6792](https://github.com/pocketbase/pocketbase/discussions/6792)).
|
||||||
|
_This method could be useful in case you want to clone an existing Record file and assign it to a new Record (e.g. in a Record duplicate action)._
|
||||||
|
|
||||||
|
|
||||||
## v0.27.2
|
## v0.27.2
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -176,6 +176,18 @@ func (r *bytesReadSeekCloser) Close() error {
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
var _ FileReader = (openFuncAsReader)(nil)
|
||||||
|
|
||||||
|
// openFuncAsReader defines a FileReader from a bare Open function.
|
||||||
|
type openFuncAsReader func() (io.ReadSeekCloser, error)
|
||||||
|
|
||||||
|
// Open implements the [filesystem.FileReader] interface.
|
||||||
|
func (r openFuncAsReader) Open() (io.ReadSeekCloser, error) {
|
||||||
|
return r()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
var extInvalidCharsRegex = regexp.MustCompile(`[^\w\.\*\-\+\=\#]+`)
|
var extInvalidCharsRegex = regexp.MustCompile(`[^\w\.\*\-\+\=\#]+`)
|
||||||
|
|
||||||
const randomAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789"
|
const randomAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -30,6 +31,8 @@ import (
|
||||||
// note: the same as blob.ErrNotFound for backward compatibility with earlier versions
|
// note: the same as blob.ErrNotFound for backward compatibility with earlier versions
|
||||||
var ErrNotFound = blob.ErrNotFound
|
var ErrNotFound = blob.ErrNotFound
|
||||||
|
|
||||||
|
const metadataOriginalName = "original-filename"
|
||||||
|
|
||||||
type System struct {
|
type System struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
bucket *blob.Bucket
|
bucket *blob.Bucket
|
||||||
|
@ -123,6 +126,45 @@ func (s *System) GetFile(fileKey string) (*blob.Reader, error) {
|
||||||
return s.GetReader(fileKey)
|
return s.GetReader(fileKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetReaderAsFile constructs a new reuploadable File value from the
|
||||||
|
// associated fileKey blob.Reader.
|
||||||
|
//
|
||||||
|
// If preserveName is false then the returned File.Name will have
|
||||||
|
// a new randomly generated suffix, otherwise it will reuse the original one.
|
||||||
|
//
|
||||||
|
// This method could be useful in case you want to clone an existing
|
||||||
|
// Record file and assign it to a new Record (e.g. in a Record duplicate action).
|
||||||
|
//
|
||||||
|
// If you simply want to copy an existing file to a new location you
|
||||||
|
// could check the Copy(srcKey, dstKey) method.
|
||||||
|
func (s *System) GetReuploadableFile(fileKey string, preserveName bool) (*File, error) {
|
||||||
|
attrs, err := s.Attributes(fileKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name := path.Base(fileKey)
|
||||||
|
originalName := attrs.Metadata[metadataOriginalName]
|
||||||
|
if originalName == "" {
|
||||||
|
originalName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
file := &File{}
|
||||||
|
file.Size = attrs.Size
|
||||||
|
file.OriginalName = originalName
|
||||||
|
file.Reader = openFuncAsReader(func() (io.ReadSeekCloser, error) {
|
||||||
|
return s.GetReader(fileKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
if preserveName {
|
||||||
|
file.Name = name
|
||||||
|
} else {
|
||||||
|
file.Name = normalizeName(file.Reader, originalName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Copy copies the file stored at srcKey to dstKey.
|
// Copy copies the file stored at srcKey to dstKey.
|
||||||
//
|
//
|
||||||
// If srcKey file doesn't exist, it returns ErrNotFound.
|
// If srcKey file doesn't exist, it returns ErrNotFound.
|
||||||
|
@ -197,7 +239,7 @@ func (s *System) UploadFile(file *File, fileKey string) error {
|
||||||
opts := &blob.WriterOptions{
|
opts := &blob.WriterOptions{
|
||||||
ContentType: mt.String(),
|
ContentType: mt.String(),
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
"original-filename": originalName,
|
metadataOriginalName: originalName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +281,7 @@ func (s *System) UploadMultipart(fh *multipart.FileHeader, fileKey string) error
|
||||||
opts := &blob.WriterOptions{
|
opts := &blob.WriterOptions{
|
||||||
ContentType: mt.String(),
|
ContentType: mt.String(),
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
"original-filename": originalName,
|
metadataOriginalName: originalName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -578,6 +578,83 @@ func TestFileSystemGetReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileSystemGetReuploadableFile(t *testing.T) {
|
||||||
|
dir := createTestDir(t)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
fsys, err := filesystem.NewLocal(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer fsys.Close()
|
||||||
|
|
||||||
|
t.Run("missing.txt", func(t *testing.T) {
|
||||||
|
_, err := fsys.GetReuploadableFile("missing.txt", false)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected error, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
testReader := func(t *testing.T, f *filesystem.File, expectedContent string) {
|
||||||
|
r, err := f.Reader.Open()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rawStr := string(raw)
|
||||||
|
|
||||||
|
if rawStr != expectedContent {
|
||||||
|
t.Fatalf("Expected content %q, got %q", expectedContent, rawStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("existing (preserve name)", func(t *testing.T) {
|
||||||
|
file, err := fsys.GetReuploadableFile("test/sub1.txt", true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := file.OriginalName; v != "sub1.txt" {
|
||||||
|
t.Fatalf("Expected originalName %q, got %q", "sub1.txt", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := file.Size; v != 4 {
|
||||||
|
t.Fatalf("Expected size %d, got %d", 4, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := file.Name; v != "sub1.txt" {
|
||||||
|
t.Fatalf("Expected name to be preserved, got %q", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
testReader(t, file, "sub1")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("existing (new random suffix name)", func(t *testing.T) {
|
||||||
|
file, err := fsys.GetReuploadableFile("test/sub1.txt", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := file.OriginalName; v != "sub1.txt" {
|
||||||
|
t.Fatalf("Expected originalName %q, got %q", "sub1.txt", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := file.Size; v != 4 {
|
||||||
|
t.Fatalf("Expected size %d, got %d", 4, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := file.Name; v == "sub1.txt" || len(v) <= len("sub1.txt.png") {
|
||||||
|
t.Fatalf("Expected name to have new random suffix, got %q", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
testReader(t, file, "sub1")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestFileSystemCopy(t *testing.T) {
|
func TestFileSystemCopy(t *testing.T) {
|
||||||
dir := createTestDir(t)
|
dir := createTestDir(t)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
Loading…
Reference in New Issue