Ответ 1
Carrierwave - отличный инструмент для обработки загрузки файлов и может справиться с этим для вас.
https://github.com/jnicklas/carrierwave#making-uploads-work-across-form-redisplays
У меня есть форма с несколькими загрузками файлов. Проблема заключается в том, что когда я отправляю форму и возникает ошибка проверки, поле ввода файла получает reset.
Я в основном хотел сохранить эти файлы в поле ввода файла для полного процесса.
Я также просмотрел несколько ссылок
Как я могу "сохранить" ? загруженное изображение при ошибке проверки формы?
Пожалуйста, дайте мне знать, каковы различные варианты в таких случаях, за которыми можно следовать.
Carrierwave - отличный инструмент для обработки загрузки файлов и может справиться с этим для вас.
https://github.com/jnicklas/carrierwave#making-uploads-work-across-form-redisplays
Создал репо с примером использования скрепки на рельсах и продолжением ваших файлов при ошибке проверки
Мне пришлось исправить это в недавнем проекте, используя Gem Paperclip. Это немного хаки, но это работает. Я пробовал вызывать cache_images(), используя after_validation и before_save в модели, но он не удается создать по какой-то причине, что я не могу определить, поэтому я просто вызываю его из контроллера. Надеюсь, это поможет кому-то еще некоторое время!
Модель:
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
контроллер:
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
Вид:
= f.file_field :logo
= f.hidden_field :logo_cache
- if @shop.logo.file?
%img{src: @shop.logo.url, alt: ''}
Ну, я подумал о другом подходе к этому; Вместо того, чтобы временно хранить файл на сервере, почему бы не вернуть его обратно клиенту, чтобы его повторно отправить, когда пользователь исправляет проблемы проверки.
Это может по-прежнему нуждаться в некоторой доработке, но это общая концепция:
# in the controller - save the file and its attributes to params
def create
# ...
if params[:doc] # a regular file uploaded through the file form element
# when the form re-renders, it will have those additional params available to it
params[:uploaded_file] = params[:doc].read # File contents
params[:uploaded_file_original_filename] = params[:doc].original_filename
params[:uploaded_file_headers] = params[:doc].headers
params[:uploaded_file_content_type] = params[:doc].content_type
elsif params[:uploaded_file] # a file coming through the form-resubmit
# generate an ActionDispatch::Http::UploadedFile
tempfile = Tempfile.new("#{params[:uploaded_file_original_filename]}-#{Time.now}")
tempfile.binmode
tempfile.write CGI.unescape(params[:uploaded_file]) #content of the file / unescaped
tempfile.close
# merge into the params
params.merge!(doc:
ActionDispatch::Http::UploadedFile.new(
:tempfile => tempfile,
:filename => params[:uploaded_file_original_filename],
:head => params[:uploaded_file_headers],
:type => params[:uploaded_file_content_type]
)
)
end
#...
# params (including the UploadedFile) can be used to generate and save the model object
end
# in the form (haml)
- if !params[:uploaded_file].blank?
# file contents in hidden textarea element
= text_area_tag(:uploaded_file, CGI.escape(params[:uploaded_file]), style: 'display: none;') #escape the file content
= hidden_field_tag :uploaded_file_headers, params[:uploaded_file_headers]
= hidden_field_tag :uploaded_file_content_type, params[:uploaded_file_content_type]
= hidden_field_tag :uploaded_file_original_filename, params[:uploaded_file_original_filename]
Обходным решением для этого, а не прямого решения является использование проверки на стороне клиента, чтобы файл не был потерян, потому что сохраняется вся форма.
Несколько пользователей, у которых нет встроенного JavaScript, будут потерять файлы между запросами, но, возможно, этот процент настолько низок для вас, чтобы сделать его приемлемым компромиссом. Если это маршрут, который вы решили спуститься, я бы рекомендовал этот камень
https://github.com/bcardarella/client_side_validations
Что делает весь процесс действительно простым и означает, что вам не нужно переписывать свою проверку в JavaScript
Я применил совершенно другой подход к другим предлагаемым здесь решениям, так как я не хотел переключиться на CarrierWave или использовать еще один камень для реализации взлома, чтобы обойти это.
В основном, я определяю заполнители для сообщений об ошибках проверки, а затем делаю вызов AJAX соответствующему контроллеру. если он не прошел проверку, я просто заполняю заполнители сообщений об ошибках - это оставляет все на месте клиентскую сторону, включая входной файл, готовый для повторной отправки.
Пример, демонстрирующий организацию с вложенной моделью адреса и модель вложенного логотипа (с прикрепленным файлом) - это сокращено для краткости:
организации /_form.html.erb
<%= form_for @organisation, html: {class: 'form-horizontal', role: 'form', multipart: true}, remote: true do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<p class='name error_explanation'></p>
<%= f.fields_for :operational_address do |fa| %>
<%= fa.label :postcode %>
<%= fa.text_field :postcode %>
<p class='operational_address postcode error_explanation'></p>
<% end %>
<%= f.fields_for :logo do |fl| %>
<%= fl.file_field :image %>
<p class='logo image error_explanation'></p>
<% end %>
<% end %>
organisations_controller.rb
def create
if @organisation.save
render :js => "window.location = '#{organisations_path}'"
else
render :validation_errors
end
end
организации /validation _errors.js.erb
$('.error_explanation').html('');
<% @organisation.errors.messages.each do |attribute, messages| %>
$('.<%= attribute %>.error_explanation').html("<%= messages.map{|message| "'#{message}'"}.join(', ') %>");
<% end %>