ruby-on-rails - problemas - numero de referencia clip
No perder el accesorio del clip cuando no se puede guardar el modelo debido a un error de validaciĆ³n (8)
A partir de septiembre de 2013, el clip no tiene la intención de "arreglar" la pérdida de archivos adjuntos después de la validación. "El problema es (IMHO) más fácil y más correctamente evitado que resuelto"
https://github.com/thoughtbot/paperclip/issues/72#issuecomment-24072728
Estoy considerando la solución CarrierWave propuesta en la solución anterior de John Gibb
El escenario es un modelo normal que contiene un adjunto de clip junto con algunas otras columnas que tienen varias validaciones. Cuando no se puede guardar un formulario para crear un objeto debido a un error de validación no relacionado con el archivo adjunto, las columnas como cadenas se conservan y se rellenan previamente para el usuario, pero el archivo seleccionado para la carga se pierde por completo y el usuario debe volver a seleccionarlo.
¿Existe un enfoque estándar para preservar el archivo adjunto en el caso de un error de validación del modelo? Esto parece un caso de uso muy común.
Parece poco elegante hackear una solución donde el archivo se guarda sin un propietario y luego se vuelve a conectar al objeto después de que se haya guardado con éxito, así que espero evitarlo.
Cambie a utilizar CarrierWave. Sé que esto estaba en un comentario, pero acabo de pasar todo el día haciendo la transición, por lo que mi respuesta puede ser útil todavía.
Primero, puede seguir un gran panorama sobre la configuración de wave wave: http://railscasts.com/episodes/253-carrierwave-file-uploads
Para lograr que se conserve la imagen entre las publicaciones, debe agregar un campo oculto con el sufijo ''caché'':
<%= form_for @user, :html => {:multipart => true} do |f| %>
<p>
<label>My Avatar</label>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</p>
<% end %>
Para Heroku
Y si está implementando en Heroku como lo soy yo, necesita hacer algunos cambios para que funcione, ya que el almacenamiento en caché funciona al guardar temporalmente las cargas en un directorio llamado public / uploads. Dado que el sistema de archivos es de solo lectura en Heroku, necesita que use la carpeta tmp en su lugar, y que el rack sirva archivos estáticos desde allí.
Dile a carrierwave que use la carpeta tmp para el almacenamiento en caché.
En su config / initializers / carrierwave.rb (siéntase libre de crear si no está allí), agregue:
CarrierWave.configure do |config|
config.root = Rails.root.join(''tmp'')
config.cache_dir = ''carrierwave''
end
Configure el bastidor para servir archivos estáticos desde la carpeta tmp / carrierwave
En su archivo config.ru, agregue:
use Rack::Static, :urls => [''/carrierwave''], :root => ''tmp''
Para ver un ejemplo de una aplicación barebones rails / carrierwave / s3 / heroku completamente funcional, echa un vistazo a:
https://github.com/trevorturk/carrierwave-heroku (sin afiliación, solo fue útil).
¡Espero que esto ayude!
En el archivo de vista, simplemente ponga si la condición debe aceptar solo el registro que tenía una identificación válida. En mi escenario este es el fragmento de código
<p>Uploaded files:</p>
<ul>
<% @user.org.crew.w9_files.each do |file| %>
<% if file.id.present? %>
<li> <%= rails code to display value %> </li>
<% end %>
<% end %>
</ul>
Guarda tu foto primero que prueba el resto
Digamos que tienes un usuario con un avatar de clip:
def update
@user = current_user
unless params[:user][:avatar].nil?
@user.update_attributes(avatar: params[:user][:avatar])
params[:user].delete :avatar
end
if @user.update_attributes(params[:user])
redirect_to edit_profile_path, notice: ''User was successfully updated.''
else
render action: "edit"
end
end
Si no se requiere la imagen, ¿por qué no dividir el formulario en dos etapas, la primera crea el objeto, la segunda página le permite agregar información opcional (como una foto)?
Alternativamente, puede validar el formulario a medida que el usuario ingresa la información para que no tenga que enviar el formulario para descubrir que sus datos no son válidos.
Siguiendo la idea de @galatians, obtuve esta solución (y trabajé muy bien)
Creé un repositorio para ese ejemplo: * https://github.com/mariohmol/paperclip-keeponvalidation
- Lo primero que debe hacer es poner algunos métodos en su registro activo base, para que cada modelo que use el adjunto pueda hacer que funcione
En config / initializers / active_record.rb
module ActiveRecord
class Base
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
#ex: @avatar_cache = cache_files(avatar,@avatar_cache)
def cache_files(avatar,avatar_cache)
if avatar.queued_for_write[:original]
FileUtils.cp(avatar.queued_for_write[:original].path, avatar.path(:original))
avatar_cache = encrypt(avatar.path(:original))
elsif avatar_cache.present?
File.open(decrypt(avatar_cache)) {|f| assign_attributes(avatar: f)}
end
return avatar_cache
end
end
end
- Después de eso, incluya en su modelo y campo adjunto, el código anterior
En el ejemplo, lo incluí en /models/users.rb
has_attached_file :avatar, PaperclipUtils.config
attr_accessor :avatar_cache
def cache_images
@avatar_cache=cache_files(avatar,@avatar_cache)
end
En su controlador, agregue esto para obtener de la memoria caché la imagen (justo antes del punto donde guarda el modelo)
@ user.avatar_cache = params [: usuario] [: avatar_cache]
@ user.cache_images
@ user.save
Y finalmente incluya esto en su vista, para registrar la ubicación de la imagen temporal actual
f.hidden_field: avatar_cache
- Si desea mostrar en la vista el archivo real, inclúyalo:
<% if @user.avatar.exists? %> <label class="field">Actual Image </label> <div class="field file-field"> <%= image_tag @user.avatar.url %> </div> <% end %>
También refile ver refile (opción más reciente)
Caracteristicas :
- Backends configurables, sistema de archivos, S3, etc ...
- Conveniente integración con ORMs
- Manipulación de imágenes y otros archivos sobre la marcha.
- Streaming de IO para cargas rápidas y con memoria
- Funciona en redisplay de formularios, es decir, cuando fallan las validaciones, incluso en S3
- Cargas directas sin esfuerzo, incluso a S3
- Soporte para múltiples cargas de archivos
Tuve que arreglar esto en un proyecto reciente usando PaperClip. He intentado llamar cache_images () usando after_validation y before_save en el modelo, pero falla en la creación por alguna razón que no puedo determinar, así que solo lo llamo desde el controlador.
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
ver:
= f.file_field :logo
= f.hidden_field :logo_cache
- if @shop.logo.file?
%img{src: @shop.logo.url, alt: ''''}