Поиск по этому блогу

9 октября 2010 г.

Rails 3 + paperclip + SWFUpload

На сайте возникла необходимость добавлять по несколько фотографий за раз. Для такой задачи подошел SWF Upload, но не сразу, так как первоначально скачанная версия отказалась работать во Flash Player 10 из-за изменений в политике безопасности. Поэтому брать все скрипты пришлось отсюда.

Чтобы контроллер смог одновременно принимать файл из формы и файлы из флеша, делаем следующее:
controllers/photos_controller.rb:
   def create
    if params[:Filedata]
      @photo = Photo.new(:swfupload_file => params[:Filedata])
      if @photo.save
        render :text => 'photo'
      else
        render :text => "error"
      end
    else
      photo = Photo.create(params[:photo])
      redirect_to photos_path, :notice => "Фотография добавлена"
    end
  end
опуская обычную форму, привожу флешевую
views/images/form.haml:
  %div{:class => "fieldset flash", :id => "fsUploadProgress"}
    %span.legend Очередь загрузки
  %div#Status 0 файлов загружено
  %div
    %table
      %tr
        %td
          %span#spanButtonPlaceHolder Не работает, что-то не так с яваскриптом или флешем :(
        %td
          %input#btnCancel{:type => "button", :value => "Отменить все загрузки", :onclick => "swfu.cancelQueue();", :disabled => "disabled", :style => "margin-left: 2px; font-size: 8pt; height: 29px;"}
=render 'swfu'
этот кусок инлайн-js я выделил для удобства в отдельный файл
_swfu.erb:
<script src="/swfu/swfupload.js" type="text/javascript"></script>
<script src="/swfu/swfupload.queue.js" type="text/javascript"></script>
<script src="/swfu/fileprogress.js" type="text/javascript"></script>
<script src="/swfu/handlers.js" type="text/javascript"></script>
<link href="/swfu/swfupload.css" rel="stylesheet" type="text/css" />

<script type="text/javascript">
var swfu;
window.onload = function() {
    var settings = {
        flash_url : "/swfu/swfupload.swf",
        upload_url: "<%= photos_path %>",
        post_params: {
          "<%= key = Rails.application.config.session_options[:key] %>" : "<%= cookies[key] %>",
      "<%= request_forgery_protection_token %>" : "<%= form_authenticity_token %>"
          },
        file_size_limit : "100 MB",
        file_types : "*.*",
        file_types_description : "All Files",
        file_upload_limit : 100,
        file_queue_limit : 0,
        custom_settings : {
            progressTarget : "fsUploadProgress",
            cancelButtonId : "btnCancel"
        },
        debug: false,
        // Button settings
        button_image_url: "/swfu/TestImageNoText_65x29.png",
        button_width: "65",
        button_height: "29",
        button_placeholder_id: "spanButtonPlaceHolder",
        button_text: '<span>Загрузить</span>',
        button_text_style: ".theFont { font-size: 16; }",
        button_text_left_padding: 8,
        button_text_top_padding: 3,
       
        // The event handler functions are defined in handlers.js
        file_queued_handler : fileQueued,
        file_queue_error_handler : fileQueueError,
        file_dialog_complete_handler : fileDialogComplete,
        upload_start_handler : uploadStart,
        upload_progress_handler : uploadProgress,
        upload_error_handler : uploadError,
        upload_success_handler : uploadSuccess,
        upload_complete_handler : uploadComplete,
        queue_complete_handler : queueComplete    // Queue plugin event
        };
    swfu = new SWFUpload(settings);
    };
</script>
Как тут можно увидеть, флеш отсылает параметры для того, чтоб было понятно, что это за юзер шлет нам файло. Было настоящей головной болью заставить флеш авторизоваться в Rails приложении именно на стороне сервера, потому как ну не происходит магии превращения переменных в полноценную сессию. Как выход из ситуации создаем
app/middleware/flash_session_cookie_middleware.rb
require 'rack/utils'
class FlashSessionCookieMiddleware
  def initialize(app, session_key = '_session_id')
    @app = app
    @session_key = session_key
  end
  def call(env)
    if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/
      req = Rack::Request.new(env)
      env['HTTP_COOKIE'] = [ @session_key, req.params[@session_key] ].join('=').freeze unless req.params[@session_key].nil?
      env['HTTP_ACCEPT'] = "#{req.params['_http_accept']}".freeze unless req.params['_http_accept'].nil?
    end
    @app.call(env)
  end
end
В config/application.rb надо добавить в автозагрузку путь "app/middleware".
   %w(observers mailers middleware).each do |dir|
      config.autoload_paths << "#{config.root}/app/#{dir}"
    end
config/initializers/session_store.rb в конец:
Rails.application.config.middleware.insert_before(
  ActionDispatch::Session::CookieStore,
  FlashSessionCookieMiddleware,
  Rails.application.config.session_options[:key]
)
Теперь файл нормально доходит до контроллера, но модель пока не знает, что такое swfupload_file=
models/photo.rb
  has_attached_file :image, :styles => {:thumbnail => '100x100#', :big => '800x600>'}
  def swfupload_file=(data)
    data.content_type = MIME::Types.type_for(data.original_filename).to_s
    self.image = data
  end
Теперь всё должно работать. Единственное, где лично я споткнулся, это хранилище сессий, оно должно быть именно Cookies, или копайтесь дальше сами.

Комментариев нет:

Отправить комментарий