2009-11-03 11 views
31

que tenía una pregunta rápida. ¿Es posible guardar un archivo sin cargarlo realmente a través de un formulario?Almacenamiento de archivos utilizando Paperclip sin carga

Por ejemplo, digamos que estoy mirando a los archivos adjuntos de los correos electrónicos, y quiero guardarlas usando un clip. ¿Cómo hago esto? ¿Tengo que llamar manualmente a save_file (o algo similar) en alguna parte?

¡Cualquier ayuda sería muy apreciada!

Respuesta

46

tengo una tarea rake que carga imágenes (logotipos del cliente) de un directorio directamente sobre parperclip. Probablemente pueda adaptarlo a sus necesidades.

Este es mi modelo simplificado de cliente:

class Client < ActiveRecord::Base 
    LOGO_STYLES = { 
    :original => ['1024x768>', :jpg], 
    :medium => ['256x192#', :jpg], 
    :small => ['128x96#', :jpg] 
    } 

    has_attached_file :logo, 
    :styles => Client::LOGO_STYLES, 
    :url => "/clients/logo/:id.jpg?style=:style" 
    attr_protected :logo_file_name, :logo_content_type, :logo_size 

Entonces en mi tarea rastrillo hago esto:

# the logos are in a folder with path logos_dir 
Dir.glob(File.join(logos_dir,'*')).each do |logo_path| 
    if File.basename(logo_path)[0]!= '.' and !File.directory? logo_path 

    client_code = File.basename(logo_path, '.*') #filename without extension 
    client = Client.find_by_code(client_code) #you could use the ids, too 
    raise "could not find client for client_code #{client_code}" if client.nil? 

    File.open(logo_path) do |f| 
     client.logo = f # just assign the logo attribute to a file 
     client.save 
    end #file gets closed automatically here 
    end 
end 

Saludos!

+4

El uso de File.new (ruta) conduce a situaciones no deseadas. Paperclip nunca cierra la instancia File.new y esto puede generar errores como "Demasiados archivos abiertos" al procesar muchos archivos adjuntos. El código correcto debe ser 'f = File.new (logo_path) client.logo = f f.close' –

+2

Muy buen comentario. No acerté ese problema porque lo usé en una tarea muy pequeña con una pequeña cantidad de archivos. He actualizado mi solución: prefiero utilizar File.open siempre que sea posible en lugar de cerrar manualmente. – kikito

11

El archivo guardado en Paperclip no tiene que ser cargado directamente a través de un formulario.

estoy usando Paperclip en un proyecto para guardar archivos de direcciones URL de los resultados WebCrawler. No estoy seguro de cómo obtendrás los archivos adjuntos de correo electrónico (¿están en el sistema de archivos local del servidor? ¿Es tu aplicación una aplicación de correo electrónico como GMail?) Pero siempre y cuando obtengas un archivo (a través de algo como open(URI.parse(crawl_result)) en mi caso ...) puede adjuntar ese archivo a su campo modelo que está marcado has_attached_file.

Esta entrada de blog sobre Easy Upload via URL with Paperclip me ayudó a resolver esto.

Desde ahora parece el blog original ya no está disponible - aquí está el quid de la cuestión sacado de Wayback Machine:

Este ejemplo muestra un modelo de la foto que tiene un archivo adjunto de imagen.

La técnica que estamos utilizando requiere agregar una columna *_remote_url (cadena) para su archivo adjunto, que se utiliza para almacenar la URL original. Entonces, en este caso, necesitamos agregar una columna llamada image_remote_url la tabla de fotos.

# db/migrate/20081210200032_add_image_remote_url_to_photos.rb 

class AddImageRemoteUrlToPhotos < ActiveRecord::Migration 
    def self.up 
    add_column :photos, :image_remote_url, :string 
    end 

    def self.down 
    remove_column :photos, :image_remote_url 
    end 
end 

Nada especial que se requiere para el controlador ...

# app/controllers/photos_controller.rb 

class PhotosController < ApplicationController 

    def create 
    @photo = Photo.new(params[:photo]) 
    if @photo.save 
     redirect_to photos_path 
    else 
     render :action => 'new' 
    end 
    end 

end 

En el formulario, añadimos una llama text_field :image_url, por lo que la gente puede subir un archivo o una URL ...

# app/views/photos/new.html.erb 

<%= error_messages_for :photo %> 
<% form_for :photo, :html => { :multipart => true } do |f| %> 
    Upload a photo: <%= f.file_field :image %><br> 
    ...or provide a URL: <%= f.text_field :image_url %><br> 
    <%= f.submit 'Submit' %> 
<% end %> 

la materia carnosa está en el modelo de la foto. Necesitamos require open-uri, agregar un attr_accessor :image_url, y hacer las cosas normales has_attached_file. A continuación, añadimos una devolución de llamada before_validation para descargar el archivo en el atributo image_url (si se proporciona) y guardar la URL original como image_remote_url. Finalmente, hacemos un validates_presence_of :image_remote_url, que nos permite rescatar de las muchas excepciones que pueden surgir al intentar descargar el archivo.

# app/models/photo.rb 

require 'open-uri' 

class Photo < ActiveRecord::Base 

    attr_accessor :image_url 

    has_attached_file :image # etc... 

    before_validation :download_remote_image, :if => :image_url_provided? 

    validates_presence_of :image_remote_url, :if => :image_url_provided?, :message => 'is invalid or inaccessible' 

private 

    def image_url_provided? 
    !self.image_url.blank? 
    end 

    def download_remote_image 
    self.image = do_download_remote_image 
    self.image_remote_url = image_url 
    end 

    def do_download_remote_image 
    io = open(URI.parse(image_url)) 
    def io.original_filename; base_uri.path.split('/').last; end 
    io.original_filename.blank? ? nil : io 
    rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...) 
    end 

end 

Todo va a trabajar de forma normal, incluyendo la creación de miniaturas, etc. Además, puesto que estamos haciendo todas las cosas difíciles en el modelo, "subir" un archivo a través de URL funciona desde dentro de la escritura/consola así:

$ script/console 
Loading development environment (Rails 2.2.2) 
>> Photo.new(:image_url => 'http://www.google.com/intl/en_ALL/images/logo.gif') 
=> #<Photo image_file_name: "logo.gif", image_remote_url: "http://www.google.com/intl/en_ALL/images/logo.gif"> 
+0

¡Dulce! ¡Muchas gracias! –

+0

@Nate Gracias por este comentario. Parece que el enlace ya no funciona? –

+0

Esto consume bastante memoria para descargar archivos de gran tamaño. En segundo lugar, se devuelve un StringIO para archivos de menos de 10 KB y si el clip hace la validación del tipo de contenido, esto fallará porque las cadenas no tienen tipos de contenido. – maletor

Cuestiones relacionadas