factory.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package imagedata
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/base64"
  6. "io"
  7. "net/http"
  8. "os"
  9. "github.com/imgproxy/imgproxy/v3/asyncbuffer"
  10. "github.com/imgproxy/imgproxy/v3/fetcher"
  11. "github.com/imgproxy/imgproxy/v3/imagetype"
  12. )
  13. // Factory represents ImageData factory
  14. type Factory struct {
  15. fetcher *fetcher.Fetcher
  16. }
  17. // NewFactory creates a new factory
  18. func NewFactory(fetcher *fetcher.Fetcher) *Factory {
  19. return &Factory{
  20. fetcher: fetcher,
  21. }
  22. }
  23. // NewFromBytesWithFormat creates a new ImageData instance from the provided format
  24. // and byte slice.
  25. func NewFromBytesWithFormat(format imagetype.Type, b []byte) ImageData {
  26. return &imageDataBytes{
  27. data: b,
  28. format: format,
  29. cancel: nil,
  30. }
  31. }
  32. // NewFromBytes creates a new ImageData instance from the provided byte slice.
  33. func (f *Factory) NewFromBytes(b []byte) (ImageData, error) {
  34. r := bytes.NewReader(b)
  35. format, err := imagetype.Detect(r)
  36. if err != nil {
  37. return nil, err
  38. }
  39. return NewFromBytesWithFormat(format, b), nil
  40. }
  41. // NewFromPath creates a new ImageData from an os.File
  42. func (f *Factory) NewFromPath(path string) (ImageData, error) {
  43. fl, err := os.Open(path)
  44. if err != nil {
  45. return nil, err
  46. }
  47. defer fl.Close()
  48. b, err := io.ReadAll(fl)
  49. if err != nil {
  50. return nil, err
  51. }
  52. return f.NewFromBytes(b)
  53. }
  54. // NewFromBase64 creates a new ImageData from a base64 encoded byte slice
  55. func (f *Factory) NewFromBase64(encoded string) (ImageData, error) {
  56. b, err := base64.StdEncoding.DecodeString(encoded)
  57. if err != nil {
  58. return nil, err
  59. }
  60. return f.NewFromBytes(b)
  61. }
  62. // sendRequest is a common logic between sync and async download.
  63. func (f *Factory) sendRequest(ctx context.Context, url string, opts DownloadOptions) (*fetcher.Request, *http.Response, http.Header, error) {
  64. h := make(http.Header)
  65. // NOTE: This will be removed in the future when our test context gets better isolation
  66. if len(redirectAllRequestsTo) > 0 {
  67. url = redirectAllRequestsTo
  68. }
  69. req, err := f.fetcher.BuildRequest(ctx, url, opts.Header, opts.CookieJar)
  70. if err != nil {
  71. return req, nil, h, err
  72. }
  73. res, err := req.Fetch()
  74. if res != nil {
  75. h = res.Header.Clone()
  76. }
  77. if err != nil {
  78. if res != nil {
  79. res.Body.Close()
  80. }
  81. req.Cancel()
  82. return req, nil, h, err
  83. }
  84. res, err = limitResponseSize(res, opts.MaxSrcFileSize)
  85. if err != nil {
  86. if res != nil {
  87. res.Body.Close()
  88. }
  89. req.Cancel()
  90. return req, nil, h, err
  91. }
  92. return req, res, h, nil
  93. }
  94. // DownloadSync downloads the image synchronously and returns the ImageData and HTTP headers.
  95. func (f *Factory) DownloadSync(ctx context.Context, imageURL, desc string, opts DownloadOptions) (ImageData, http.Header, error) {
  96. if opts.DownloadFinished != nil {
  97. defer opts.DownloadFinished()
  98. }
  99. req, res, h, err := f.sendRequest(ctx, imageURL, opts)
  100. if res != nil {
  101. defer res.Body.Close()
  102. }
  103. if req != nil {
  104. defer req.Cancel()
  105. }
  106. if err != nil {
  107. return nil, h, wrapDownloadError(err, desc)
  108. }
  109. b, err := io.ReadAll(res.Body)
  110. if err != nil {
  111. return nil, h, wrapDownloadError(err, desc)
  112. }
  113. format, err := imagetype.Detect(bytes.NewReader(b))
  114. if err != nil {
  115. return nil, h, wrapDownloadError(err, desc)
  116. }
  117. d := NewFromBytesWithFormat(format, b)
  118. return d, h, nil
  119. }
  120. // DownloadAsync downloads the image asynchronously and returns the ImageData
  121. // backed by AsyncBuffer and HTTP headers.
  122. func (f *Factory) DownloadAsync(ctx context.Context, imageURL, desc string, opts DownloadOptions) (ImageData, http.Header, error) {
  123. // We pass this responsibility to AsyncBuffer
  124. //nolint:bodyclose
  125. req, res, h, err := f.sendRequest(ctx, imageURL, opts)
  126. if err != nil {
  127. if opts.DownloadFinished != nil {
  128. defer opts.DownloadFinished()
  129. }
  130. return nil, h, wrapDownloadError(err, desc)
  131. }
  132. b := asyncbuffer.New(res.Body, int(res.ContentLength), opts.DownloadFinished)
  133. format, err := imagetype.Detect(b.Reader())
  134. if err != nil {
  135. b.Close()
  136. req.Cancel()
  137. return nil, h, wrapDownloadError(err, desc)
  138. }
  139. d := &imageDataAsyncBuffer{
  140. b: b,
  141. format: format,
  142. desc: desc,
  143. cancel: nil,
  144. }
  145. d.AddCancel(req.Cancel) // request will be closed when the image data is consumed
  146. return d, h, nil
  147. }