image_data.go 3.3 KB

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