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 }