¿Cómo puedo descargar un archivo desde una URL y guardarlo en Rails?

197

Tengo una URL a una imagen que quiero guardar localmente, para poder usar Paperclip para producir una miniatura para mi aplicación. ¿Cuál es la mejor manera de descargar y guardar la imagen? (Investigué el manejo del archivo ruby ​​pero no encontré nada).

Alok Swain
fuente

Respuestas:

315

Prueba esto:

require 'open-uri'
open('image.png', 'wb') do |file|
  file << open('http://example.com/image.png').read
end
Levi
fuente
1
Esto funciona. Gracias. Intenté esto en una imagen. La imagen no se escribió correctamente en el archivo. Como si hubiera sido corrompido o algo así. También podría guiarme para hacer esto mismo en una aplicación Rails. ¿Puedo usar esto en una aplicación Rails?
Alok Swain
Sí, puede usarlo en una aplicación de rieles. El archivo no se escribe correctamente probablemente porque olvidé abrir el destino en modo de escritura binaria. Lo editaré ahora.
Levi
78
Esto lee todo el archivo en la memoria. La mejor manera esIO.copy_stream(open('http://example.com/image.png'), 'destination.png')
Janko-m
3
rieles 5, 2016, no necesitaba hacerlo require 'open-uri', solo usé el código debajo de él
Mirror318
1
Esta respuesta no me funcionó. la respuesta correcta está aquí: stackoverflow.com/a/29743394/1536309
Blair Anderson
147

Una versión aún más corta:

require 'open-uri'
download = open('http://example.com/image.png')
IO.copy_stream(download, '~/image.png')

Para mantener el mismo nombre de archivo:

IO.copy_stream(download, "~/#{download.base_uri.to_s.split('/')[-1]}")
Yelmo de Clemens
fuente
2
Y más eficiente ya que la salida no se almacena como una cadena en la memoria. +1
Itay Grudev
2
Aunque en realidad es útil tener ambas respuestas mostradas, para que las personas como yo puedan ver ambas, pensar en las diferencias y luego elegir esta respuesta conscientemente por las razones correctas :)
Maxwell Evans
30

Si está utilizando PaperClip, la descarga desde una URL ahora se gestiona automáticamente.

Asumiendo que tienes algo como:

class MyModel < ActiveRecord::Base
  has_attached_file :image, ...
end

En su modelo, simplemente especifique la imagen como una URL, algo como esto (escrito en letra deliberada):

@my_model = MyModel.new
image_url = params[:image_url]
@my_model.image = URI.parse(image_url)

Probablemente quiera poner esto en un método en su modelo. Esto también funcionará bien en el sistema de archivos temporal de Heroku.

Paperclip lo tomará desde allí.

fuente: documentación de clip

superluminario
fuente
44
Para futuros buscadores, Paperclip ya no permite esto de forma predeterminada: medium.com/in-the-weeds/…
Alex Dunae
14

Creo que esta es la forma más clara:

require 'open-uri'

File.write 'image.png', open('http://example.com/image.png').read
Sabio ross
fuente
Sage soy nuevo en los carriles, donde puedo añadir este código en el modelo o el controlador o en la vista
adarsh
1
Depende, pero normalmente harías algo así dentro de un modelo o método de controlador. En un contexto de Rails, probablemente sea mejor usar las herramientas del marco como ActiveStorage, en lugar de escribir directamente en el disco.
Sage Ross el
10

Echa un vistazo a Net :: HTTP en la biblioteca estándar. La documentación proporciona varios ejemplos sobre cómo descargar documentos usando HTTP.

Pär Wieslander
fuente
2
Esta respuesta necesita más visibilidad, ya que Kernel#openpermite no solo el acceso a los archivos, sino también la invocación del proceso mediante el prefijo de un símbolo de tubería (por ejemplo, open("| ls")). Por lo tanto, puede conducir a un grave riesgo de seguridad al usar entradas variables para el argumento de Kernel#open.
ConorB