72 lines
2.0 KiB
Go
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
|
|
}
|