ruby-on-railsimagemagickminimagickshrine

Creating a MiniMagick Montage on the fly from images uploaded with Shrine to S3


I am trying to create a montage file composed of various thumbnails uploaded (privately) to S3 with Shrine.

The code for this action resides in a Sidekiq worker (at Heroku) which should send an email with the above processed image as an email attachment. (Then just dumps the image)

Here is my attempt:

images = []

@user.photos.first(25).each do |photo|
  images << File.read(photo.image[:thumb].url)
end

processed_image = MiniMagick::Tool::Montage.new do |image|
        images.each {|i| image << i} 
        image.tile "5x5"
        image << "output.jpg"        
end

attachments.inline['images.jpg'] = processed_image

Though I get error :

2019-11-04T18:17:59.638Z 30695 TID-ot0uksdbv WARN: Errno::ENOENT: No such file or directory @ rb_sysopen - https://mysite.s3.eu-west-1.amazonaws.com/photo/thumb/5cb924406fa8944e5279a15b46f250f6.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIJJCEGJCEVP2A%2F20191104%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20191104T181759Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=38706b7526fd0a8095a2f387521063d4d8901c4523696ff7e1f60ae2d

It seems it can't open the thumb at S3 (I have obfuscated the link above, but when pasted in browser it returns the image correctly)

I have tried to pass the link directly in the block :

images << photo.image[:thumb].url

But this time it seems I get errors from MiniMagick

montage-im6.q16: not authorized HTTPS' @ error/delegate.c/InvokeDelegate/1717. montage-im6.q16: unable to open file': No such file or directory @ error/constitute.c/ReadImage/544. montage-im6.q16: not authorized HTTPS' @ error/delegate.c/InvokeDelegate/1717. montage-im6.q16: unable to open file': Operation not permitted @ error/constitute.c/ReadImage/544. montage-im6.q16: not authorized HTTPS' @ error/delegate.c/InvokeDelegate/1717. montage-im6.q16: unable to open file': Operation not permitted @ error/constitute.c/ReadImage/544. montage-im6.q16: not authorized HTTPS' @ error/delegate.c/InvokeDelegate/1717. montage-im6.q16: unable to open file': Operation not permitted @ error/constitute.c/ReadImage/544. montage-im6.q16: not authorized `HTTPS' @ error/delegate.c/InvokeDelegate/1717.

Not very sure how to handle this..


Solution

  • You need to download the files to disk before processing, and use the local paths in your montage command. Also, you'll need to read the result file from the output path, as the montage command returns only stdout.

    Together it could look something like this:

    # download input images to disk
    input_images = @user.photos.first(25)
      .map { |photo| photo.image[:thumb] }
      .map(&:download)
    
    # create temporary file for output image
    processed_image = Tempfile.new ["montage", ".jpg"], binmode: true
    
    MiniMagick::Tool::Montage.new do |montage|
      montage.merge! input_images.map(&:path)
      montage.tile "5x5"
      montage << processed_image.path
    end
    
    attachments.inline['images.jpg'] = File.binread(processed_image.path)
    
    # close and delete temporary files
    [*input_images, processed_image].each(&:close!)