Rails ActiveStorage: Attach a Vips Image

I'm struggling to attach a Vips:Image object to an ActiveStorage object.

I use Vips to compress a PNG image. I'm then looking to save this compressed PNG version into a second ActiveStorage attachment. The code is failing when attempting to attach. All I get is: Process finished with exit code -1073741819 (0xC0000005)

# compress the image and save the compressed version of the file as a PNG
# both img and img_to_compress are active storage attachments
def compress_charlie(img, img_to_compress)

  # load the image as a Vips Image object
  vips_img = Vips::Image.new_from_buffer (URI.open(img.url) { |f| f.read }), ''

  # do compression, etc ... not bothering to show this code as it has no impact on the issue I have

  # save the compressed png
  img_to_compress.attach(
    io: StringIO.new(vips_img .write_to_buffer('.png')),
    filename: img.filename
  )

end

Any help is appreciated, Charlie

Ruby 3.1 Rails 7.0.1


You're decompressing and recompressing twice. How about (untested):

def convert_to_png(img, img_to_compress)
  # load the image as a Vips Image object
  vips_img = Vips::Image.new_from_buffer (URI.open(img.url) { |f| f.read }), ''

  # save as a PNG string
  png = StringIO.new(vips_img.write_to_buffer('.png')),

  img_to_compress.attach(io: png, filename: img.filename)
end

pngsave_buffer gives you a String with binary encoding, you don't need to save a second time. Try this test program:

require "vips"

x = Vips::Image.new_from_file ARGV[0]
y = x.pngsave_buffer

puts "y.class = #{y.class}"

I see:

$ ./pngsave.rb ~/pics/k2.jpg
y.class = String

If the image is already a PNG you'll be wasting a lot of time, of course. You could add something to detect the format and skip the conversion.