I have a URL to an image which i want to save locally, so that I can use Paperclip to produce a thumbnail for my application. What's the best way to download and save the image? (I looked into ruby file handling but did not come across anything.)
8 Answers
Try this:
require 'open-uri'
open('image.png', 'wb') do |file|
file << open('http://example.com/image.png').read
end
-
1This works. Thanks. I tried this on an image. The image was not properly written to the file. As if it has been corrupted or something. Also could you guide me to do this same in a Rails application. Can i use this in a Rails application ? – Alok Swain Mar 26 '10 at 03:49
-
Yes, you can use it in a rails application. The file not being written properly is probably because I forgot to open the destination in write-binary mode. I'll edit that now. – Levi Mar 26 '10 at 04:16
-
96This reads the whole file in memory. The best way is `IO.copy_stream(open('http://example.com/image.png'), 'destination.png')` – Janko Sep 24 '15 at 22:10
-
this doesn't work for me. I've created a question about this: http://stackoverflow.com/questions/37071931/how-to-save-image-from-url-to-local-ubuntu-folder-from-rails-console – user2950593 May 06 '16 at 12:18
-
3rails 5, 2016, I didn't need to `require 'open-uri'`, just used the code under it – Mirror318 Dec 16 '16 at 02:41
-
1this answer did not work for me. correct answer is here: http://stackoverflow.com/a/29743394/1536309 – Blair Anderson Mar 10 '17 at 00:49
-
Is it possible to provide a path of where the file should be stored? Currently, is being stored on root in Rails app – Steven Aguilar Dec 05 '18 at 21:01
-
Extreme caution if the url can be provided by the user! https://sakurity.com/blog/2015/02/28/openuri.html – Tashows Jan 31 '21 at 19:10
An even shorter version:
require 'open-uri'
download = open('http://example.com/image.png')
IO.copy_stream(download, '~/image.png')
To keep the same filename:
IO.copy_stream(download, "~/#{download.base_uri.to_s.split('/')[-1]}")

- 39,950
- 26
- 134
- 184

- 3,841
- 1
- 22
- 13
-
4And more efficient as the output is not stored as a string in memory. +1 – Itay Grudev Jun 03 '16 at 18:56
-
3Though it's actually useful having both answers displayed, so people like me can see both, think about the differences and then choose this answer consciously for the right reasons :) – Maxwell Evans Mar 17 '17 at 18:25
-
8I'm getting a security warning from Rubocop when using `open`. I replaced it with `URI.parse(url).open` instead. Read more about that cop here: https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Security/Open – Joshua Pinter Oct 22 '20 at 14:58
-
2
If you're using PaperClip, downloading from a URL is now handled automatically.
Assuming you've got something like:
class MyModel < ActiveRecord::Base
has_attached_file :image, ...
end
On your model, just specify the image as a URL, something like this (written in deliberate longhand):
@my_model = MyModel.new
image_url = params[:image_url]
@my_model.image = URI.parse(image_url)
You'll probably want to put this in a method in your model. This will also work just fine on Heroku's temporary filesystem.
Paperclip will take it from there.
source: paperclip documentation

- 47,086
- 25
- 151
- 148
-
5For future searchers, Paperclip no longer allows this by default: https://medium.com/in-the-weeds/all-about-paperclips-cve-2017-0889-server-side-request-forgery-ssrf-vulnerability-8cb2b1c96fe8 – Alex Dunae Jan 31 '18 at 01:04
I think this is the clearest way:
require 'open-uri'
File.write 'image.png', open('http://example.com/image.png').read

- 561
- 5
- 12
-
sage i am new to rails, where i can add this code in model or controller or in view – adarsh Jan 09 '19 at 05:30
-
1It depends, but typically you'd do something like this inside a model or controller method. In a Rails context, you're likely better off using the framework's tools like ActiveStorage, rather than writing directly to disk. – Sage Ross Jan 09 '19 at 17:24
Check out Net::HTTP in the standard library. The documentation provides several examples on how to download documents using HTTP.

- 28,374
- 7
- 55
- 54
-
2This answer needs more visibility as `Kernel#open` enables not only file access but also process invocation by prefixing a pipe symbol (e.g., `open("| ls")`). So, it may lead to a serious security risk by using variable input to the argument of `Kernel#open`. – Conor May 31 '18 at 11:25
Possibly the simplest way:
require 'open-uri'
image_url = "https://i.imgur.com/ZWnhY9T.png"
IO.copy_stream(URI.open(image_url), 'destination.png')

- 41,291
- 27
- 223
- 311
-
1In my situation, I tried all the codes above. only this floor worked. The difference is copy_stream()'s first param **URI.open(img_url)**, if i dont wrap the `img_url` by `URI.open()`, i got the following ERROR `No such file or directory @ rb_sysopen`. – lee mcMcartney Mar 02 '22 at 06:27
Using Ruby 3 and above, you'll get the following error using the accepted answer:
No such file or directory @ rb_sysopen - http://example.com/image.png (Errno::ENOENT)
The solution is to use URI.open
in place of the Kernel.open
. Example:
require "uri"
download = URI.open('http://example.com/image.png')
File.write('~/image.png', download)

- 10,657
- 14
- 59
- 80
All above examples are great. In my case I just wanted to create a download link from the image from URL.
If you want to make it downloadable (to your downloads folder), you can use the following code in your controller:
require 'open-uri'
file_type = url.to_s.split(".")[-1]
send_data open(url).read, filename: "some_name.#{file_type}", type: "image/#{file_type}", disposition: "attachment"

- 33
- 6