2021-04-26 13:52:50 +02:00
|
|
|
package imagedata
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2021-09-30 16:23:30 +02:00
|
|
|
"github.com/imgproxy/imgproxy/v3/config"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/ierrors"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/imagetype"
|
2023-02-23 20:11:44 +02:00
|
|
|
"github.com/imgproxy/imgproxy/v3/security"
|
2021-04-26 13:52:50 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
Watermark *ImageData
|
|
|
|
FallbackImage *ImageData
|
|
|
|
)
|
|
|
|
|
|
|
|
type ImageData struct {
|
|
|
|
Type imagetype.Type
|
|
|
|
Data []byte
|
|
|
|
Headers map[string]string
|
|
|
|
|
|
|
|
cancel context.CancelFunc
|
|
|
|
cancelOnce sync.Once
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *ImageData) Close() {
|
|
|
|
d.cancelOnce.Do(func() {
|
|
|
|
if d.cancel != nil {
|
|
|
|
d.cancel()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *ImageData) SetCancel(cancel context.CancelFunc) {
|
|
|
|
d.cancel = cancel
|
|
|
|
}
|
|
|
|
|
|
|
|
func Init() error {
|
|
|
|
initRead()
|
|
|
|
|
|
|
|
if err := initDownloading(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := loadWatermark(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := loadFallbackImage(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadWatermark() (err error) {
|
|
|
|
if len(config.WatermarkData) > 0 {
|
2023-02-23 20:11:44 +02:00
|
|
|
Watermark, err = FromBase64(config.WatermarkData, "watermark", security.DefaultOptions())
|
2021-04-26 13:52:50 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.WatermarkPath) > 0 {
|
2023-02-23 20:11:44 +02:00
|
|
|
Watermark, err = FromFile(config.WatermarkPath, "watermark", security.DefaultOptions())
|
2021-04-26 13:52:50 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.WatermarkURL) > 0 {
|
2023-03-21 19:58:16 +02:00
|
|
|
Watermark, err = Download(context.Background(), config.WatermarkURL, "watermark", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions())
|
2021-04-26 13:52:50 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadFallbackImage() (err error) {
|
2022-03-31 09:36:13 +02:00
|
|
|
switch {
|
|
|
|
case len(config.FallbackImageData) > 0:
|
2023-02-23 20:11:44 +02:00
|
|
|
FallbackImage, err = FromBase64(config.FallbackImageData, "fallback image", security.DefaultOptions())
|
2022-03-31 09:36:13 +02:00
|
|
|
case len(config.FallbackImagePath) > 0:
|
2023-02-23 20:11:44 +02:00
|
|
|
FallbackImage, err = FromFile(config.FallbackImagePath, "fallback image", security.DefaultOptions())
|
2022-03-31 09:36:13 +02:00
|
|
|
case len(config.FallbackImageURL) > 0:
|
2023-03-21 19:58:16 +02:00
|
|
|
FallbackImage, err = Download(context.Background(), config.FallbackImageURL, "fallback image", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions())
|
2022-03-31 09:36:13 +02:00
|
|
|
default:
|
|
|
|
FallbackImage, err = nil, nil
|
2021-04-26 13:52:50 +02:00
|
|
|
}
|
|
|
|
|
2022-03-31 09:36:13 +02:00
|
|
|
if FallbackImage != nil && err == nil && config.FallbackImageTTL > 0 {
|
|
|
|
if FallbackImage.Headers == nil {
|
|
|
|
FallbackImage.Headers = make(map[string]string)
|
|
|
|
}
|
|
|
|
FallbackImage.Headers["Fallback-Image"] = "1"
|
2021-04-26 13:52:50 +02:00
|
|
|
}
|
|
|
|
|
2022-03-31 09:36:13 +02:00
|
|
|
return err
|
2021-04-26 13:52:50 +02:00
|
|
|
}
|
|
|
|
|
2023-02-23 20:11:44 +02:00
|
|
|
func FromBase64(encoded, desc string, secopts security.Options) (*ImageData, error) {
|
2021-04-26 13:52:50 +02:00
|
|
|
dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encoded))
|
|
|
|
size := 4 * (len(encoded)/3 + 1)
|
|
|
|
|
2023-02-23 20:11:44 +02:00
|
|
|
imgdata, err := readAndCheckImage(dec, size, secopts)
|
2021-04-26 13:52:50 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Can't decode %s: %s", desc, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return imgdata, nil
|
|
|
|
}
|
|
|
|
|
2023-02-23 20:11:44 +02:00
|
|
|
func FromFile(path, desc string, secopts security.Options) (*ImageData, error) {
|
2021-04-26 13:52:50 +02:00
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Can't read %s: %s", desc, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fi, err := f.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Can't read %s: %s", desc, err)
|
|
|
|
}
|
|
|
|
|
2023-02-23 20:11:44 +02:00
|
|
|
imgdata, err := readAndCheckImage(f, int(fi.Size()), secopts)
|
2021-04-26 13:52:50 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Can't read %s: %s", desc, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return imgdata, nil
|
|
|
|
}
|
|
|
|
|
2023-03-21 19:58:16 +02:00
|
|
|
func Download(ctx context.Context, imageURL, desc string, opts DownloadOptions, secopts security.Options) (*ImageData, error) {
|
|
|
|
imgdata, err := download(ctx, imageURL, opts, secopts)
|
2021-04-26 13:52:50 +02:00
|
|
|
if err != nil {
|
2021-10-13 13:59:46 +02:00
|
|
|
if nmErr, ok := err.(*ErrorNotModified); ok {
|
|
|
|
nmErr.Message = fmt.Sprintf("Can't download %s: %s", desc, nmErr.Message)
|
|
|
|
return nil, nmErr
|
|
|
|
}
|
|
|
|
return nil, ierrors.WrapWithPrefix(err, 1, fmt.Sprintf("Can't download %s", desc))
|
2021-04-26 13:52:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return imgdata, nil
|
|
|
|
}
|