Running with Ruby

Tag: Tempfile

Rails + Paperclip + Open-Uri – Downloading files from the internet and saving them with Paperclip

I don’t like hotlinking. Being dependent on someone’s else infrastructure makes me a bit worried. Especially when I don’t know who this “someone” is. That’s why I’ve decided to download all the images that users embed in comments. After download, I attach them with Paperclip to their comments. Thanks to that, I can be sure, that they will always be served from my own servers (also I can use them later for some fancy stuff).

Theoretically, to obtain this, we could do something like that:

# We will use open-uri to download embedded images
require "open-uri"

file = open(image_url)
current_comment.pictures.create!(file: file)

Unfortunately this will “almost” work. If we try to get the file url:

current_picture.file.url #=> '/images/pictures/12313/file.'

we’ll get it without an file extension. This happens, because open-uri creates a Tempfile without a file extension. Unluckily Paperclip uses file name to determine extension. To get around this issue, we need to create a new Tempfile with a valid extension and copy open-uri Tempfile content to newly created one:

# We will use open-uri to download embedded images
require "open-uri"

file = Tempfile.new ['', ".#{image_url.split('.').last}"]
file.binmode # note that our tempfile must be in binary mode
file.write open(image_url).read
file.rewind
file
current_comment.pictures.create!(file: file)

Update – Paperclip can do this on its own!

Posted by Aditya Sanghi (thanks a lot!):

current_comments.pictures.create!(file: URI.parse(image_url))

Although keep in mind, that you still need to handle 500, 404, etc errors (Paperclip can raise them).

Ruby Tempfile extension without random postfix

Recently I fell into the “RFM” trap (read fu**ing manual). I had some Ajax files uploaded to my controller and to make it easier, I’ve been processing them using Tempfile class. This was not a problem (I’ve been processing their content) until I’ve decided to do an Ajax file upload which was connected to paperclip. Paperclip saves the file name with extension so when using with Tempfile instance, you might get the invalid file extension.

temp = Tempfile.new(params[:file_name])
temp.binmode
temp.write(Base64.decode64(file[:file_value]))
temp.rewind
temp

The file name is obtained with method path:

temp = Tempfile.new('demo.jpg')
temp.path #=> #<Tempfile:/tmp/demo.jpg20130403-3747-13d19dx>
user.avatar = temp
user.save!

# Paperclip object instance
user.avatar(:big) #=> '/images/users/user_avatar.jpg20130403-3747-13d19dx'

This might be a problem, because some browser won’t render image with invalid extension. However fixing this issue is really easy. It all comes down to reading the manual.

You can provide an Array instance with two elements as a first parameter. The temporary file base name will begin with the array’s first element, and end with the second element:

temp = Tempfile.new(['demo', '.jpg'])
temp.path #=> #<Tempfile:/tmp/demo20130403-3747-13d19dx.jpg>
user.avatar = temp
user.save!

Conclusion

Since this was mentioned in documentation I’ve got a lesson to always read it :)

Copyright © 2018 Running with Ruby

Theme by Anders NorenUp ↑