image_data.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package imagedata
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/base64"
  6. "fmt"
  7. "io"
  8. "os"
  9. "strings"
  10. "sync"
  11. "github.com/imgproxy/imgproxy/v3/config"
  12. "github.com/imgproxy/imgproxy/v3/ierrors"
  13. "github.com/imgproxy/imgproxy/v3/imagemeta"
  14. "github.com/imgproxy/imgproxy/v3/imagetype"
  15. "github.com/imgproxy/imgproxy/v3/security"
  16. )
  17. var (
  18. Watermark *ImageData
  19. FallbackImage *ImageData
  20. )
  21. type ImageData struct {
  22. meta imagemeta.Meta
  23. data []byte
  24. Headers map[string]string
  25. cancel context.CancelFunc
  26. cancelOnce sync.Once
  27. }
  28. func (d *ImageData) Close() error {
  29. d.cancelOnce.Do(func() {
  30. if d.cancel != nil {
  31. d.cancel()
  32. }
  33. })
  34. return nil
  35. }
  36. // Meta returns the image metadata
  37. func (d *ImageData) Meta() imagemeta.Meta {
  38. return d.meta
  39. }
  40. // Format returns the image format based on the metadata
  41. func (d *ImageData) Format() imagetype.Type {
  42. return d.meta.Format()
  43. }
  44. // Reader returns an io.ReadSeeker for the image data
  45. func (d *ImageData) Reader() io.ReadSeeker {
  46. return bytes.NewReader(d.data)
  47. }
  48. // Size returns the size of the image data in bytes.
  49. // NOTE: asyncbuffer implementation will .Wait() for the data to be fully read
  50. func (d *ImageData) Size() (int, error) {
  51. return len(d.data), nil
  52. }
  53. func (d *ImageData) SetCancel(cancel context.CancelFunc) {
  54. d.cancel = cancel
  55. }
  56. func Init() error {
  57. initRead()
  58. if err := initDownloading(); err != nil {
  59. return err
  60. }
  61. if err := loadWatermark(); err != nil {
  62. return err
  63. }
  64. if err := loadFallbackImage(); err != nil {
  65. return err
  66. }
  67. return nil
  68. }
  69. func loadWatermark() (err error) {
  70. if len(config.WatermarkData) > 0 {
  71. Watermark, err = FromBase64(config.WatermarkData, "watermark", security.DefaultOptions())
  72. return
  73. }
  74. if len(config.WatermarkPath) > 0 {
  75. Watermark, err = FromFile(config.WatermarkPath, "watermark", security.DefaultOptions())
  76. return
  77. }
  78. if len(config.WatermarkURL) > 0 {
  79. Watermark, err = Download(context.Background(), config.WatermarkURL, "watermark", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions())
  80. return
  81. }
  82. return nil
  83. }
  84. func loadFallbackImage() (err error) {
  85. switch {
  86. case len(config.FallbackImageData) > 0:
  87. FallbackImage, err = FromBase64(config.FallbackImageData, "fallback image", security.DefaultOptions())
  88. case len(config.FallbackImagePath) > 0:
  89. FallbackImage, err = FromFile(config.FallbackImagePath, "fallback image", security.DefaultOptions())
  90. case len(config.FallbackImageURL) > 0:
  91. FallbackImage, err = Download(context.Background(), config.FallbackImageURL, "fallback image", DownloadOptions{Header: nil, CookieJar: nil}, security.DefaultOptions())
  92. default:
  93. FallbackImage, err = nil, nil
  94. }
  95. if FallbackImage != nil && err == nil && config.FallbackImageTTL > 0 {
  96. if FallbackImage.Headers == nil {
  97. FallbackImage.Headers = make(map[string]string)
  98. }
  99. FallbackImage.Headers["Fallback-Image"] = "1"
  100. }
  101. return err
  102. }
  103. func FromBase64(encoded, desc string, secopts security.Options) (*ImageData, error) {
  104. dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encoded))
  105. size := 4 * (len(encoded)/3 + 1)
  106. imgdata, err := readAndCheckImage(dec, size, secopts)
  107. if err != nil {
  108. return nil, fmt.Errorf("Can't decode %s: %s", desc, err)
  109. }
  110. return imgdata, nil
  111. }
  112. func FromFile(path, desc string, secopts security.Options) (*ImageData, error) {
  113. f, err := os.Open(path)
  114. if err != nil {
  115. return nil, fmt.Errorf("Can't read %s: %s", desc, err)
  116. }
  117. fi, err := f.Stat()
  118. if err != nil {
  119. return nil, fmt.Errorf("Can't read %s: %s", desc, err)
  120. }
  121. imgdata, err := readAndCheckImage(f, int(fi.Size()), secopts)
  122. if err != nil {
  123. return nil, fmt.Errorf("Can't read %s: %s", desc, err)
  124. }
  125. return imgdata, nil
  126. }
  127. func Download(ctx context.Context, imageURL, desc string, opts DownloadOptions, secopts security.Options) (*ImageData, error) {
  128. imgdata, err := download(ctx, imageURL, opts, secopts)
  129. if err != nil {
  130. return nil, ierrors.Wrap(
  131. err, 0,
  132. ierrors.WithPrefix(fmt.Sprintf("Can't download %s", desc)),
  133. )
  134. }
  135. return imgdata, nil
  136. }