Files
NianAIGC/backend/internal/assets/image_format.go
2026-09-23 13:33:47 +08:00

72 lines
2.0 KiB
Go

package assets
import (
"bufio"
"errors"
"io"
"net/http"
"path"
"strings"
)
// normalizeImageFile uses the bytes, not a provider URL suffix or an untrusted
// Content-Type, as evidence of a recognized raster format. It does not decode
// or transcode the image. Unrecognized data retains its original metadata.
func normalizeImageFile(name, contentType string, prefix []byte) (string, string) {
if len(prefix) > 512 {
prefix = prefix[:512]
}
detected := http.DetectContentType(prefix)
var extension string
switch detected {
case "image/png":
extension = ".png"
case "image/jpeg":
extension = ".jpg"
case "image/webp":
extension = ".webp"
case "image/gif":
extension = ".gif"
case "image/bmp":
extension = ".bmp"
case "image/x-icon":
extension = ".ico"
default:
return name, contentType
}
name = strings.TrimSpace(name)
existing := path.Ext(name)
lower := strings.ToLower(existing)
if lower == extension || detected == "image/jpeg" && (lower == ".jpeg" || lower == ".jpe") {
return name, detected
}
base := strings.TrimSuffix(name, existing)
if base == "" || base == "." || base == "/" {
base = "image"
}
return base + extension, detected
}
// NormalizeImageDownload fixes legacy filenames and response MIME types from
// a bounded prefix. The returned body streams every original byte, and Close
// still closes the underlying OSS/local/remote stream. No catalog/object write
// is performed, including for historical .image objects.
func NormalizeImageDownload(name string, blob Blob) (string, Blob, error) {
if blob.Body == nil {
return name, blob, ErrBlobNotFound
}
reader := bufio.NewReaderSize(blob.Body, 512)
prefix, err := reader.Peek(512)
if err != nil && !errors.Is(err, io.EOF) {
return name, blob, err
}
name, blob.ContentType = normalizeImageFile(name, blob.ContentType, prefix)
blob.Body = &imageReadCloser{Reader: reader, Closer: blob.Body}
return name, blob, nil
}
type imageReadCloser struct {
io.Reader
io.Closer
}