store the correct image/png as attrs content type when generating a thumb fallback

This commit is contained in:
Gani Georgiev
2025-12-09 17:09:32 +02:00
parent abb6bcd6de
commit e89603497f
4 changed files with 68 additions and 23 deletions

View File

@@ -1,3 +1,8 @@
## v0.34.3 (WIP)
- Store the correct `image/png` as attrs content type when generating a thumb fallback _(e.g. for `webp`)_.
## v0.34.2 ## v0.34.2
- Bumped JS SDK to v0.26.5 to fix Safari AbortError detection introduced with the previous release ([#7369](https://github.com/pocketbase/pocketbase/issues/7369)). - Bumped JS SDK to v0.26.5 to fix Safari AbortError detection introduced with the previous release ([#7369](https://github.com/pocketbase/pocketbase/issues/7369)).

View File

@@ -52,8 +52,8 @@ func TestNewFileFromPath(t *testing.T) {
} }
// existing file // existing file
originalName := "image_! noext" originalName := "image_!@ special"
normalizedNamePattern := regexp.QuoteMeta("image_noext_") + `\w{10}` + regexp.QuoteMeta(".png") normalizedNamePattern := regexp.QuoteMeta("image_special_") + `\w{10}` + regexp.QuoteMeta(".png")
f, err := filesystem.NewFileFromPath(filepath.Join(testDir, originalName)) f, err := filesystem.NewFileFromPath(filepath.Join(testDir, originalName))
if err != nil { if err != nil {
t.Fatalf("Expected nil error, got %v", err) t.Fatalf("Expected nil error, got %v", err)
@@ -83,8 +83,8 @@ func TestNewFileFromBytes(t *testing.T) {
t.Fatal("Expected error, got nil") t.Fatal("Expected error, got nil")
} }
originalName := "image_! noext" originalName := "image_!@ special"
normalizedNamePattern := regexp.QuoteMeta("image_noext_") + `\w{10}` + regexp.QuoteMeta(".txt") normalizedNamePattern := regexp.QuoteMeta("image_special_") + `\w{10}` + regexp.QuoteMeta(".txt")
f, err := filesystem.NewFileFromBytes([]byte("text\n"), originalName) f, err := filesystem.NewFileFromBytes([]byte("text\n"), originalName)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -175,8 +175,8 @@ func TestNewFileFromURLTimeout(t *testing.T) {
// valid response // valid response
{ {
originalName := "image_! noext" originalName := "image_!@ special"
normalizedNamePattern := regexp.QuoteMeta("image_noext_") + `\w{10}` + regexp.QuoteMeta(".txt") normalizedNamePattern := regexp.QuoteMeta("image_special_") + `\w{10}` + regexp.QuoteMeta(".txt")
f, err := filesystem.NewFileFromURL(context.Background(), srv.URL+"/"+originalName) f, err := filesystem.NewFileFromURL(context.Background(), srv.URL+"/"+originalName)
if err != nil { if err != nil {

View File

@@ -536,25 +536,38 @@ func (s *System) CreateThumb(originalKey string, thumbKey, thumbSize string) err
} }
} }
originalContentType := r.ContentType()
opts := &blob.WriterOptions{ opts := &blob.WriterOptions{
ContentType: r.ContentType(), ContentType: originalContentType,
} }
// open a thumb storage writer (aka. prepare for upload) var format imaging.Format
w, writerErr := s.bucket.NewWriter(s.ctx, thumbKey, opts)
if writerErr != nil {
return writerErr
}
// try to detect the thumb format based on the original file name switch originalContentType {
// (fallbacks to png on error) case "image/jpeg":
format, err := imaging.FormatFromFilename(thumbKey) format = imaging.JPEG
if err != nil { case "image/gif":
format = imaging.GIF
case "image/tiff":
format = imaging.TIFF
case "image/bmp":
format = imaging.BMP
default:
// fallback to PNG (this includes webp!)
opts.ContentType = "image/png"
format = imaging.PNG format = imaging.PNG
} }
// open a thumb storage writer (aka. prepare for upload)
w, err := s.bucket.NewWriter(s.ctx, thumbKey, opts)
if err != nil {
return err
}
// thumb encode (aka. upload) // thumb encode (aka. upload)
if err := imaging.Encode(w, thumbImg, format); err != nil { err = imaging.Encode(w, thumbImg, format)
if err != nil {
w.Close() w.Close()
return err return err
} }

View File

@@ -709,7 +709,8 @@ func TestFileSystemList(t *testing.T) {
"image.jpg", "image.jpg",
"image.svg", "image.svg",
"image.webp", "image.webp",
"image_! noext", "image_!@ special",
"image_noext",
"style.css", "style.css",
"main.js", "main.js",
"main.mjs", "main.mjs",
@@ -861,6 +862,8 @@ func TestFileSystemCreateThumb(t *testing.T) {
{"image.jpg", "thumb.jpg", "100x100", "image/jpeg"}, {"image.jpg", "thumb.jpg", "100x100", "image/jpeg"},
// webp (should produce png) // webp (should produce png)
{"image.webp", "thumb.webp", "100x100", "image/png"}, {"image.webp", "thumb.webp", "100x100", "image/png"},
// without extension (should extract the mimetype from its stored ContentType)
{"image_noext", "image_noext.jpeg", "100x100", "image/jpeg"},
} }
for _, s := range scenarios { for _, s := range scenarios {
@@ -884,13 +887,20 @@ func TestFileSystemCreateThumb(t *testing.T) {
} }
defer f.Close() defer f.Close()
attrsMimeType := f.ContentType()
mt, err := mimetype.DetectReader(f) mt, err := mimetype.DetectReader(f)
if err != nil { if err != nil {
t.Fatalf("Failed to detect thumb %s mimetype (%v)", s.thumb, err) t.Fatalf("Failed to detect thumb %s mimetype (%v)", s.thumb, err)
} }
fileMimeType := mt.String()
if mtStr := mt.String(); mtStr != s.expectedMimeType { if fileMimeType != s.expectedMimeType {
t.Fatalf("Expected thumb %s MimeType %q, got %q", s.thumb, s.expectedMimeType, mtStr) t.Fatalf("Expected thumb file %s MimeType %q, got %q", s.thumb, s.expectedMimeType, fileMimeType)
}
if attrsMimeType != s.expectedMimeType {
t.Fatalf("Expected thumb attrs %s MimeType %q, got %q", s.thumb, s.expectedMimeType, attrsMimeType)
} }
}) })
} }
@@ -975,16 +985,33 @@ func createTestDir(t *testing.T) string {
} }
} }
// no extension and invalid characters // invalid/special characters
{ {
file, err := os.OpenFile(filepath.Join(dir, "image_! noext"), os.O_WRONLY|os.O_CREATE, 0644) file, err := os.OpenFile(filepath.Join(dir, "image_!@ special"), os.O_WRONLY|os.O_CREATE, 0644)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_ = png.Encode(file, image.Rect(0, 0, 1, 1)) // tiny 1x1 png imgRect := image.Rect(0, 0, 1, 1) // tiny 1x1 png
_ = png.Encode(file, imgRect)
file.Close() file.Close()
} }
// no extension
{
fullPath := filepath.Join(dir, "image_noext")
file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
t.Fatal(err)
}
imgRect := image.Rect(0, 0, 1, 1) // tiny 1x1 jpg
_ = jpeg.Encode(file, imgRect, nil)
file.Close()
err = os.WriteFile(fullPath+".attrs", []byte(`{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"image/jpeg","user.metadata":null}`), 0644)
if err != nil {
t.Fatal(err)
}
}
// css // css
{ {
file, err := os.OpenFile(filepath.Join(dir, "style.css"), os.O_WRONLY|os.O_CREATE, 0644) file, err := os.OpenFile(filepath.Join(dir, "style.css"), os.O_WRONLY|os.O_CREATE, 0644)