2012-06-22 9 views
8

He definido un modelo story con un archivo adjunto image manejado por Paperclip, que se parece a:Paperclip (3.0) y barandas (3.2): f.file_field pierde ruta del archivo en el 'nuevo' acción después de un error de validación

class Story < ActiveRecord::Base 
    has_attached_file :image # [...] 
    attr_accessible :user_id, :title, :image, :image_file_name 
    belongs_to: user 

    validates_presence_of :user_id 
    validates :title,  :length => { :maximum => 50 } 
    validates_attachment_size :image, :less_than => 2.megabytes, :unless => Proc.new { |story| story[:image].nil? } 
    # [...] 
end 

Cuando lleno mi forma de historia, que se parece a:

<%= form_for @story, html: { multipart: true } do |f| %> 
<% if @story.errors.any? %> 
<div id="error-explanation"> 
    <ul> 
     <% @story.errors.full_messages.each do |msg| %> 
     <li class="error-mess">Error: <%= msg.downcase %></li> 
     <% end %> 
    </ul> 
</div> 
<% end %> 

<%= f.text_field :title %></td> 
<%= f.file_field :image %> 
<%= f.submit t('.send') %> 

<% end %> 

Si falla la validación para un story.title demasiado tiempo, el formulario se devuelve correctamente junto con el mensaje de error adecuado y la titl no válido e ya cumplimentado, pero el file_field está ahora en blanco y tengo que volver a hacer clic para volver a seleccionar el archivo que deseo cargar.

Y aquí es cómo mi stories_controller.rb parece:

def create 
    @story = @current_user.stories.new(params[:story]) 
    if @story.save                             
    redirect_to thanks_path  
    else 
    # [email protected] so I render action 'new' again just to 
    # bang my head against this 'anomaly' 
    render action: "new" 
    end    
end 

¿Cómo puedo evitar que los usuarios tengan que volver a seleccionar el archivo a subir después de un error de validación?

+0

que estoy enfrentando el mismo problema, le parecieron una solución ?? ¡Gracias! – yorch

+0

Solo uno gráfico: cuando se selecciona una imagen, doy una respuesta de UI a través de jQuery. Dado que mi campo de imagen es generado por '<% = f.file_field: image%>' puedo usar el siguiente jQuery para activarlo: '$ (" # story_image "). Change (function() {$ (" # image_selected ") .show (300);});' Entonces, en mi opinión, tengo un div oculto "image_selected" que contiene un gif elocuente ... Si encuentras una solución _real_, ¡házmelo saber! – Darme

Respuesta

5

La forma en que las cargas de archivos HTTP funcionan en los navegadores, el archivo se ha subido a su aplicación en primer envío, por lo que debe almacenarlo en algún lugar, de modo que aún tenga acceso a un segundo formulario. (Por lo menos en PHP de un get archivo subido borrada después de que el script ha funcionado, si expresamente no ha sido movido a otro lugar -. No sé si eso se aplica para RoR también)

Usted no puede pre -hasta un tipo de entrada = campo de archivo en HTML, por razones de seguridad. E incluso si el usuario vuelve a elegir el archivo, tendrían que enviarlo por segunda vez, desperdiciando el ancho de banda del usuario.

Así que, o almacenarlo en algún lugar de presentar primero, o tratar de hacer sus validaciones en el lado del cliente también con JavaScript antes permitiendo presentar (en lo posible), de manera que se minimiza la forma sostiene que en realidad no pasan la Validación en el lado del servidor.

+2

Ok, entonces la pregunta ahora es: ¿cómo puedo recuperar un archivo cargado en una solicitud anterior en una aplicación RoR? :-) Gracias por el consejo sobre razones de seguridad, no estaba completamente enterado de eso. – Darme

1

CBroe tiene razón, la mejor solución es almacenar el archivo temporalmente. Lo que haría para hacer esto es: - Mueva el archivo a un directorio temporal y asígnele el nombre del ID del usuario que intentaba cargarlo. : cuando se publica el formulario y no se carga ningún archivo, intente utilizar el archivo temporal para ese usuario (si existe). - Si la historia se guarda con éxito, elimine los archivos temporales para ese usuario.

Creo que debería hacer el truco.

+1

Entonces la pregunta es: ¿cómo puedo hacerlo en Rails? – Darme

1

Tuve que arreglar esto en un proyecto reciente. Es un poco hacky pero funciona. Intenté llamar a cache_images() usando after_validation y before_save en el modelo pero falla al crear por algún motivo que no puedo determinar, así que simplemente lo llamo desde el controlador. ¡Ojalá esto le ahorre a alguien más algo de tiempo!

modelo:

class Shop < ActiveRecord::Base  
    attr_accessor :logo_cache 

    has_attached_file :logo 

    def cache_images 
    if logo.staged? 
     if invalid? 
     FileUtils.cp(logo.queued_for_write[:original].path, logo.path(:original)) 
     @logo_cache = encrypt(logo.path(:original)) 
     end 
    else 
     if @logo_cache.present? 
     File.open(decrypt(@logo_cache)) {|f| assign_attributes(logo: f)} 
     end 
    end 
    end 

    private 

    def decrypt(data) 
    return '' unless data.present? 
    cipher = build_cipher(:decrypt, 'mypassword') 
    cipher.update(Base64.urlsafe_decode64(data).unpack('m')[0]) + cipher.final 
    end 

    def encrypt(data) 
    return '' unless data.present? 
    cipher = build_cipher(:encrypt, 'mypassword') 
    Base64.urlsafe_encode64([cipher.update(data) + cipher.final].pack('m')) 
    end 

    def build_cipher(type, password) 
    cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').send(type) 
    cipher.pkcs5_keyivgen(password) 
    cipher 
    end 

end 

controlador:

def create 
    @shop = Shop.new(shop_params) 
    @shop.user = current_user 
    @shop.cache_images 

    if @shop.save 
    redirect_to account_path, notice: 'Shop created!' 
    else 
    render :new 
    end 
end 

def update 
    @shop = current_user.shop 
    @shop.assign_attributes(shop_params) 
    @shop.cache_images 

    if @shop.save 
    redirect_to account_path, notice: 'Shop updated.' 
    else 
    render :edit 
    end 
end 

vista:

= f.file_field :logo 
= f.hidden_field :logo_cache 

- if @shop.logo.file? 
    %img{src: @shop.logo.url, alt: ''} 
Cuestiones relacionadas