Files
imgproxy/imagedata/image_data.go
Victor Sokolov 37a6ea2bf7 IMG-49: Get rid of global watermark image and fetcher (#1515)
* Get rid of global watermark image

* imagedata.Factory, removed global Fetcher
2025-09-05 16:22:57 +02:00

127 lines
3.5 KiB
Go

package imagedata
import (
"bytes"
"context"
"io"
"sync"
"github.com/imgproxy/imgproxy/v3/asyncbuffer"
"github.com/imgproxy/imgproxy/v3/imagetype"
)
var (
Watermark ImageData
)
// ImageData represents the data of an image that can be read from a source.
// Please note that this interface can be backed by any reader, including lazy AsyncBuffer.
// There is no other way to guarantee that the data is read without errors except reading it till EOF.
type ImageData interface {
io.Closer // Close closes the image data and releases any resources held by it
Reader() io.ReadSeeker // Reader returns a new ReadSeeker for the image data
Format() imagetype.Type // Format returns the image format from the metadata (shortcut)
Size() (int, error) // Size returns the size of the image data in bytes
Error() error // Error returns any error that occurred during reading data from source
// AddCancel attaches a cancel function to the image data.
// Please note that Cancel functions must be idempotent: for instance, an implementation
// could wrap cancel into sync.Once.
AddCancel(context.CancelFunc)
}
// imageDataBytes represents image data stored in a byte slice in memory
type imageDataBytes struct {
format imagetype.Type
data []byte
cancel []context.CancelFunc
cancelOnce sync.Once
}
// imageDataAsyncBuffer is a struct that implements the ImageData interface backed by an AsyncBuffer
type imageDataAsyncBuffer struct {
b *asyncbuffer.AsyncBuffer
format imagetype.Type
desc string
cancel []context.CancelFunc
cancelOnce sync.Once
}
// Close closes the image data and releases any resources held by it
func (d *imageDataBytes) Close() error {
d.cancelOnce.Do(func() {
for _, cancel := range d.cancel {
cancel()
}
})
return nil
}
// Format returns the image format based on the metadata
func (d *imageDataBytes) Format() imagetype.Type {
return d.format
}
// Reader returns an io.ReadSeeker for the image data
func (d *imageDataBytes) Reader() io.ReadSeeker {
return bytes.NewReader(d.data)
}
// Size returns the size of the image data in bytes.
func (d *imageDataBytes) Size() (int, error) {
return len(d.data), nil
}
// AddCancel attaches a cancel function to the image data
func (d *imageDataBytes) AddCancel(cancel context.CancelFunc) {
d.cancel = append(d.cancel, cancel)
}
func (d *imageDataBytes) Error() error {
// No error handling for in-memory data, return nil
return nil
}
// Reader returns a ReadSeeker for the image data
func (d *imageDataAsyncBuffer) Reader() io.ReadSeeker {
return d.b.Reader()
}
// Close closes the response body (hence, response) and the async buffer itself
func (d *imageDataAsyncBuffer) Close() error {
d.cancelOnce.Do(func() {
d.b.Close()
for _, cancel := range d.cancel {
cancel()
}
})
return nil
}
// Format returns the image format from the metadata
func (d *imageDataAsyncBuffer) Format() imagetype.Type {
return d.format
}
// Size returns the size of the image data in bytes.
// It waits for the async buffer to finish reading.
func (d *imageDataAsyncBuffer) Size() (int, error) {
return d.b.Wait()
}
// AddCancel attaches a cancel function to the image data
func (d *imageDataAsyncBuffer) AddCancel(cancel context.CancelFunc) {
d.cancel = append(d.cancel, cancel)
}
// Error returns any error that occurred during reading data from
// async buffer or the underlying source.
func (d *imageDataAsyncBuffer) Error() error {
if err := d.b.Error(); err != nil {
return wrapDownloadError(err, d.desc)
}
return nil
}