mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-01-23 11:14:48 +02:00
Add attachment option for Content-Disposition header (#887)
* Add attachment option for Content-Disposition header Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * Add documentation to attachment option Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * Add default attachment option value Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * fix: Add default value to docs Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * fix: Rename option attachment to return_attachment, add ENV-configuration Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com>
This commit is contained in:
parent
e06dce6a49
commit
23d0807e46
@ -51,6 +51,7 @@ var (
|
|||||||
StripColorProfile bool
|
StripColorProfile bool
|
||||||
AutoRotate bool
|
AutoRotate bool
|
||||||
EnforceThumbnail bool
|
EnforceThumbnail bool
|
||||||
|
ReturnAttachment bool
|
||||||
|
|
||||||
EnableWebpDetection bool
|
EnableWebpDetection bool
|
||||||
EnforceWebp bool
|
EnforceWebp bool
|
||||||
@ -208,6 +209,7 @@ func Reset() {
|
|||||||
StripColorProfile = true
|
StripColorProfile = true
|
||||||
AutoRotate = true
|
AutoRotate = true
|
||||||
EnforceThumbnail = false
|
EnforceThumbnail = false
|
||||||
|
ReturnAttachment = false
|
||||||
|
|
||||||
EnableWebpDetection = false
|
EnableWebpDetection = false
|
||||||
EnforceWebp = false
|
EnforceWebp = false
|
||||||
@ -356,6 +358,7 @@ func Configure() error {
|
|||||||
configurators.Bool(&StripColorProfile, "IMGPROXY_STRIP_COLOR_PROFILE")
|
configurators.Bool(&StripColorProfile, "IMGPROXY_STRIP_COLOR_PROFILE")
|
||||||
configurators.Bool(&AutoRotate, "IMGPROXY_AUTO_ROTATE")
|
configurators.Bool(&AutoRotate, "IMGPROXY_AUTO_ROTATE")
|
||||||
configurators.Bool(&EnforceThumbnail, "IMGPROXY_ENFORCE_THUMBNAIL")
|
configurators.Bool(&EnforceThumbnail, "IMGPROXY_ENFORCE_THUMBNAIL")
|
||||||
|
configurators.Bool(&ReturnAttachment, "IMGPROXY_RETURN_ATTACHMENT")
|
||||||
|
|
||||||
configurators.Bool(&EnableWebpDetection, "IMGPROXY_ENABLE_WEBP_DETECTION")
|
configurators.Bool(&EnableWebpDetection, "IMGPROXY_ENABLE_WEBP_DETECTION")
|
||||||
configurators.Bool(&EnforceWebp, "IMGPROXY_ENFORCE_WEBP")
|
configurators.Bool(&EnforceWebp, "IMGPROXY_ENFORCE_WEBP")
|
||||||
|
@ -421,5 +421,6 @@ imgproxy can send logs to syslog, but this feature is disabled by default. To en
|
|||||||
* `IMGPROXY_STRIP_COLOR_PROFILE`: when `true`, imgproxy will transform the embedded color profile (ICC) to sRGB and remove it from the image. Otherwise, imgproxy will try to keep it as is. Default: `true`
|
* `IMGPROXY_STRIP_COLOR_PROFILE`: when `true`, imgproxy will transform the embedded color profile (ICC) to sRGB and remove it from the image. Otherwise, imgproxy will try to keep it as is. Default: `true`
|
||||||
* `IMGPROXY_AUTO_ROTATE`: when `true`, imgproxy will automatically rotate images based on the EXIF Orientation parameter (if available in the image meta data). The orientation tag will be removed from the image in all cases. Default: `true`
|
* `IMGPROXY_AUTO_ROTATE`: when `true`, imgproxy will automatically rotate images based on the EXIF Orientation parameter (if available in the image meta data). The orientation tag will be removed from the image in all cases. Default: `true`
|
||||||
* `IMGPROXY_ENFORCE_THUMBNAIL`: when `true` and the source image has an embedded thumbnail, imgproxy will always use the embedded thumbnail instead of the main image. Currently, only thumbnails embedded in `heic` and `avif` are supported. Default: `false`
|
* `IMGPROXY_ENFORCE_THUMBNAIL`: when `true` and the source image has an embedded thumbnail, imgproxy will always use the embedded thumbnail instead of the main image. Currently, only thumbnails embedded in `heic` and `avif` are supported. Default: `false`
|
||||||
|
* `IMGPROXY_RETURN_ATTACHMENT`: when `true`, response header `Content-Disposition` will include `attachment`. Default: `false`
|
||||||
* `IMGPROXY_HEALTH_CHECK_MESSAGE`: <i class='badge badge-pro'></i> the content of the health check response. Default: `imgproxy is running`
|
* `IMGPROXY_HEALTH_CHECK_MESSAGE`: <i class='badge badge-pro'></i> the content of the health check response. Default: `imgproxy is running`
|
||||||
* `IMGPROXY_HEALTH_CHECK_PATH`: an additional path of the health check. Default: blank
|
* `IMGPROXY_HEALTH_CHECK_PATH`: an additional path of the health check. Default: blank
|
||||||
|
@ -529,6 +529,15 @@ eth:%enforce_thumbnail
|
|||||||
|
|
||||||
When set to `1`, `t` or `true` and the source image has an embedded thumbnail, imgproxy will always use the embedded thumbnail instead of the main image. Currently, only thumbnails embedded in `heic` and `avif` are supported. This is normally controlled by the [IMGPROXY_ENFORCE_THUMBNAIL](configuration.md#miscellaneous) configuration but this procesing option allows the configuration to be set for each request.
|
When set to `1`, `t` or `true` and the source image has an embedded thumbnail, imgproxy will always use the embedded thumbnail instead of the main image. Currently, only thumbnails embedded in `heic` and `avif` are supported. This is normally controlled by the [IMGPROXY_ENFORCE_THUMBNAIL](configuration.md#miscellaneous) configuration but this procesing option allows the configuration to be set for each request.
|
||||||
|
|
||||||
|
### Return attachment
|
||||||
|
|
||||||
|
```
|
||||||
|
return_attachment:%return_attachment
|
||||||
|
att:%return_attachment
|
||||||
|
```
|
||||||
|
|
||||||
|
When set to `1`, `t` or `true`, imgproxy will return `attachment` in the `Content-Disposition` header, and the browser will open a 'Save as' dialog. This is normally controlled by the [IMGPROXY_RETURN_ATTACHMENT](configuration.md#miscellaneous) configuration but this procesing option allows the configuration to be set for each request.
|
||||||
|
|
||||||
### Quality
|
### Quality
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -54,16 +54,16 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentDispositionsFmt = map[Type]string{
|
contentDispositionsFmt = map[Type]string{
|
||||||
JPEG: "inline; filename=\"%s.jpg\"",
|
JPEG: "%s; filename=\"%s.jpg\"",
|
||||||
PNG: "inline; filename=\"%s.png\"",
|
PNG: "%s; filename=\"%s.png\"",
|
||||||
WEBP: "inline; filename=\"%s.webp\"",
|
WEBP: "%s; filename=\"%s.webp\"",
|
||||||
GIF: "inline; filename=\"%s.gif\"",
|
GIF: "%s; filename=\"%s.gif\"",
|
||||||
ICO: "inline; filename=\"%s.ico\"",
|
ICO: "%s; filename=\"%s.ico\"",
|
||||||
SVG: "inline; filename=\"%s.svg\"",
|
SVG: "%s; filename=\"%s.svg\"",
|
||||||
HEIC: "inline; filename=\"%s.heic\"",
|
HEIC: "%s; filename=\"%s.heic\"",
|
||||||
AVIF: "inline; filename=\"%s.avif\"",
|
AVIF: "%s; filename=\"%s.avif\"",
|
||||||
BMP: "inline; filename=\"%s.bmp\"",
|
BMP: "%s; filename=\"%s.bmp\"",
|
||||||
TIFF: "inline; filename=\"%s.tiff\"",
|
TIFF: "%s; filename=\"%s.tiff\"",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -93,27 +93,33 @@ func (it Type) Mime() string {
|
|||||||
return "application/octet-stream"
|
return "application/octet-stream"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it Type) ContentDisposition(filename string) string {
|
func (it Type) ContentDisposition(filename string, returnAttachment bool) string {
|
||||||
format, ok := contentDispositionsFmt[it]
|
disposition := "inline"
|
||||||
if !ok {
|
|
||||||
return "inline"
|
if returnAttachment {
|
||||||
|
disposition = "attachment"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(format, strings.ReplaceAll(filename, `"`, "%22"))
|
format, ok := contentDispositionsFmt[it]
|
||||||
|
if !ok {
|
||||||
|
return disposition
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(format, disposition, strings.ReplaceAll(filename, `"`, "%22"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it Type) ContentDispositionFromURL(imageURL string) string {
|
func (it Type) ContentDispositionFromURL(imageURL string, returnAttachment bool) string {
|
||||||
url, err := url.Parse(imageURL)
|
url, err := url.Parse(imageURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.ContentDisposition(contentDispositionFilenameFallback)
|
return it.ContentDisposition(contentDispositionFilenameFallback, returnAttachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, filename := filepath.Split(url.Path)
|
_, filename := filepath.Split(url.Path)
|
||||||
if len(filename) == 0 {
|
if len(filename) == 0 {
|
||||||
return it.ContentDisposition(contentDispositionFilenameFallback)
|
return it.ContentDisposition(contentDispositionFilenameFallback, returnAttachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
return it.ContentDisposition(strings.TrimSuffix(filename, filepath.Ext(filename)))
|
return it.ContentDisposition(strings.TrimSuffix(filename, filepath.Ext(filename)), returnAttachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it Type) SupportsAlpha() bool {
|
func (it Type) SupportsAlpha() bool {
|
||||||
|
@ -89,6 +89,7 @@ type ProcessingOptions struct {
|
|||||||
StripColorProfile bool
|
StripColorProfile bool
|
||||||
AutoRotate bool
|
AutoRotate bool
|
||||||
EnforceThumbnail bool
|
EnforceThumbnail bool
|
||||||
|
ReturnAttachment bool
|
||||||
|
|
||||||
SkipProcessingFormats []imagetype.Type
|
SkipProcessingFormats []imagetype.Type
|
||||||
|
|
||||||
@ -140,6 +141,7 @@ func NewProcessingOptions() *ProcessingOptions {
|
|||||||
StripColorProfile: config.StripColorProfile,
|
StripColorProfile: config.StripColorProfile,
|
||||||
AutoRotate: config.AutoRotate,
|
AutoRotate: config.AutoRotate,
|
||||||
EnforceThumbnail: config.EnforceThumbnail,
|
EnforceThumbnail: config.EnforceThumbnail,
|
||||||
|
ReturnAttachment: config.ReturnAttachment,
|
||||||
|
|
||||||
// Basically, we need this to update ETag when `IMGPROXY_QUALITY` is changed
|
// Basically, we need this to update ETag when `IMGPROXY_QUALITY` is changed
|
||||||
defaultQuality: config.Quality,
|
defaultQuality: config.Quality,
|
||||||
@ -859,6 +861,16 @@ func applyEnforceThumbnailOption(po *ProcessingOptions, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyReturnAttachmentOption(po *ProcessingOptions, args []string) error {
|
||||||
|
if len(args) > 1 {
|
||||||
|
return fmt.Errorf("Invalid return_attachment arguments: %v", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
po.ReturnAttachment = parseBoolOption(args[0])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func applyURLOption(po *ProcessingOptions, name string, args []string) error {
|
func applyURLOption(po *ProcessingOptions, name string, args []string) error {
|
||||||
switch name {
|
switch name {
|
||||||
case "resize", "rs":
|
case "resize", "rs":
|
||||||
@ -913,6 +925,8 @@ func applyURLOption(po *ProcessingOptions, name string, args []string) error {
|
|||||||
return applyStripColorProfileOption(po, args)
|
return applyStripColorProfileOption(po, args)
|
||||||
case "enforce_thumbnail", "eth":
|
case "enforce_thumbnail", "eth":
|
||||||
return applyEnforceThumbnailOption(po, args)
|
return applyEnforceThumbnailOption(po, args)
|
||||||
|
case "return_attachment", "att":
|
||||||
|
return applyReturnAttachmentOption(po, args)
|
||||||
// Saving options
|
// Saving options
|
||||||
case "quality", "q":
|
case "quality", "q":
|
||||||
return applyQualityOption(po, args)
|
return applyQualityOption(po, args)
|
||||||
|
@ -86,9 +86,9 @@ func setVary(rw http.ResponseWriter) {
|
|||||||
func respondWithImage(reqID string, r *http.Request, rw http.ResponseWriter, statusCode int, resultData *imagedata.ImageData, po *options.ProcessingOptions, originURL string, originData *imagedata.ImageData) {
|
func respondWithImage(reqID string, r *http.Request, rw http.ResponseWriter, statusCode int, resultData *imagedata.ImageData, po *options.ProcessingOptions, originURL string, originData *imagedata.ImageData) {
|
||||||
var contentDisposition string
|
var contentDisposition string
|
||||||
if len(po.Filename) > 0 {
|
if len(po.Filename) > 0 {
|
||||||
contentDisposition = resultData.Type.ContentDisposition(po.Filename)
|
contentDisposition = resultData.Type.ContentDisposition(po.Filename, po.ReturnAttachment)
|
||||||
} else {
|
} else {
|
||||||
contentDisposition = resultData.Type.ContentDispositionFromURL(originURL)
|
contentDisposition = resultData.Type.ContentDispositionFromURL(originURL, po.ReturnAttachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
rw.Header().Set("Content-Type", resultData.Type.Mime())
|
rw.Header().Set("Content-Type", resultData.Type.Mime())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user