image_data.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package imagedata
  2. import (
  3. "bytes"
  4. "context"
  5. "io"
  6. "sync"
  7. "github.com/imgproxy/imgproxy/v3/asyncbuffer"
  8. "github.com/imgproxy/imgproxy/v3/config"
  9. "github.com/imgproxy/imgproxy/v3/ierrors"
  10. "github.com/imgproxy/imgproxy/v3/imagetype"
  11. )
  12. var (
  13. Watermark ImageData
  14. )
  15. // ImageData represents the data of an image that can be read from a source.
  16. // Please note that this interface can be backed by any reader, including lazy AsyncBuffer.
  17. // There is no other way to guarantee that the data is read without errors except reading it till EOF.
  18. type ImageData interface {
  19. io.Closer // Close closes the image data and releases any resources held by it
  20. Reader() io.ReadSeeker // Reader returns a new ReadSeeker for the image data
  21. Format() imagetype.Type // Format returns the image format from the metadata (shortcut)
  22. Size() (int, error) // Size returns the size of the image data in bytes
  23. Error() error // Error returns any error that occurred during reading data from source
  24. // AddCancel attaches a cancel function to the image data.
  25. // Please note that Cancel functions must be idempotent: for instance, an implementation
  26. // could wrap cancel into sync.Once.
  27. AddCancel(context.CancelFunc)
  28. }
  29. // imageDataBytes represents image data stored in a byte slice in memory
  30. type imageDataBytes struct {
  31. format imagetype.Type
  32. data []byte
  33. cancel []context.CancelFunc
  34. cancelOnce sync.Once
  35. }
  36. // imageDataAsyncBuffer is a struct that implements the ImageData interface backed by an AsyncBuffer
  37. type imageDataAsyncBuffer struct {
  38. b *asyncbuffer.AsyncBuffer
  39. format imagetype.Type
  40. desc string
  41. cancel []context.CancelFunc
  42. cancelOnce sync.Once
  43. }
  44. // Close closes the image data and releases any resources held by it
  45. func (d *imageDataBytes) Close() error {
  46. d.cancelOnce.Do(func() {
  47. for _, cancel := range d.cancel {
  48. cancel()
  49. }
  50. })
  51. return nil
  52. }
  53. // Format returns the image format based on the metadata
  54. func (d *imageDataBytes) Format() imagetype.Type {
  55. return d.format
  56. }
  57. // Reader returns an io.ReadSeeker for the image data
  58. func (d *imageDataBytes) Reader() io.ReadSeeker {
  59. return bytes.NewReader(d.data)
  60. }
  61. // Size returns the size of the image data in bytes.
  62. func (d *imageDataBytes) Size() (int, error) {
  63. return len(d.data), nil
  64. }
  65. // AddCancel attaches a cancel function to the image data
  66. func (d *imageDataBytes) AddCancel(cancel context.CancelFunc) {
  67. d.cancel = append(d.cancel, cancel)
  68. }
  69. func (d *imageDataBytes) Error() error {
  70. // No error handling for in-memory data, return nil
  71. return nil
  72. }
  73. // Reader returns a ReadSeeker for the image data
  74. func (d *imageDataAsyncBuffer) Reader() io.ReadSeeker {
  75. return d.b.Reader()
  76. }
  77. // Close closes the response body (hence, response) and the async buffer itself
  78. func (d *imageDataAsyncBuffer) Close() error {
  79. d.cancelOnce.Do(func() {
  80. d.b.Close()
  81. for _, cancel := range d.cancel {
  82. cancel()
  83. }
  84. })
  85. return nil
  86. }
  87. // Format returns the image format from the metadata
  88. func (d *imageDataAsyncBuffer) Format() imagetype.Type {
  89. return d.format
  90. }
  91. // Size returns the size of the image data in bytes.
  92. // It waits for the async buffer to finish reading.
  93. func (d *imageDataAsyncBuffer) Size() (int, error) {
  94. return d.b.Wait()
  95. }
  96. // AddCancel attaches a cancel function to the image data
  97. func (d *imageDataAsyncBuffer) AddCancel(cancel context.CancelFunc) {
  98. d.cancel = append(d.cancel, cancel)
  99. }
  100. // Error returns any error that occurred during reading data from
  101. // async buffer or the underlying source.
  102. func (d *imageDataAsyncBuffer) Error() error {
  103. if err := d.b.Error(); err != nil {
  104. return wrapDownloadError(err, d.desc)
  105. }
  106. return nil
  107. }
  108. func Init() error {
  109. if err := initDownloading(); err != nil {
  110. return err
  111. }
  112. if err := loadWatermark(); err != nil {
  113. return err
  114. }
  115. return nil
  116. }
  117. func loadWatermark() error {
  118. var err error
  119. switch {
  120. case len(config.WatermarkData) > 0:
  121. Watermark, err = NewFromBase64(config.WatermarkData)
  122. // NOTE: this should be something like err = ierrors.Wrap(err).WithStackDeep(0).WithPrefix("watermark")
  123. // In the NewFromBase64 all errors should be wrapped to something like
  124. // .WithPrefix("load from base64")
  125. if err != nil {
  126. return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't load watermark from Base64"))
  127. }
  128. case len(config.WatermarkPath) > 0:
  129. Watermark, err = NewFromPath(config.WatermarkPath)
  130. if err != nil {
  131. return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't read watermark from file"))
  132. }
  133. case len(config.WatermarkURL) > 0:
  134. Watermark, _, err = DownloadSync(context.Background(), config.WatermarkURL, "watermark", DefaultDownloadOptions())
  135. if err != nil {
  136. return ierrors.Wrap(err, 0, ierrors.WithPrefix("can't download from URL"))
  137. }
  138. default:
  139. Watermark = nil
  140. }
  141. return nil
  142. }