загрузка изображений AJAX’ом на Rails 3. CarrierWave+Uploadify

Больше года назад для загрузки изображений я выбрал Paperclip, но на данный момент поддавшись модным тенденциям переходим на аяксовую форму загрузки используя гем CarrierWave и собственно саму аяксовую примочку Uploadify.

Про это уже много где написано, но все как-то разрозненно и вот я решил собрать все вместе.

CarrierWave

установка

Прежде всего надо установить RMagick и ImageMagick (здесь показан вариант для RH-линуксов):

# yum install ImageMagick ruby-RMagick ImageMagick-devel -y

Теперь вставляем в Gemfile:

gem 'carrierwave'
gem 'rmagick'

и бандлим:

bundle install

настройка в приложении

Для начала создадим тестовое приложение (на всякий случай скажу, что бандлили мы уже непосредственно в приложении, здесь же я создаю новое приложение лишь чтобы были понятны мои модели):

$ rails new Testapp
$ cd Testapp
$ rails g scaffold Picture image:string
$ rake db:migrate

После установки CarrierWave появится новый генератор uploader, чем и надо воспользоваться чтобы создать сам аплоадер:

$ rails g uploader image

После этого появиться файл app/uploaders/image_uploader.rb, где лежат настройки этого аплодера.

Теперь надо смонтировать загрузчик и модель, для этого открываем модель Picture app/models/picture.rb и вставляем туда:

mount_uploader :image, ImageUploader

хелперы форм

Форма для Picture будет выглядеть должна выглядеть примерно так:

1
2
3
4
<%= form_for(@picture,:html => {:multipart =>true}) do |f| %>
  <%= f.file_field :image%>
  <%= f.submit %>
<%end%>

Для отображения закачанного изображения используем хелпер:

1
<%= image_tag task.image_url.to_s %>

Практически все, что написано выше я собрал из двух статей: http://asci.blog.ru/120804899.html и http://wiki.dgplug.org/index.php/Deploying_of_gitorious_on_fedora, так что их можно также почитать если мой пост не ясен.

настройка загрузчика

Как я уже писал выше настройки загрузчика находяться в app/uploaders/image_uploader.rb. Там можно сконфигурировать очень многое, например директорию хранения, по умолчанию она:

1
2
3
def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end

Также очень полезной опцией являеться варианты сохранения изображений. Чтобы их было несколько надо подключить RMagick и собственно составить сами версии картинки, например:

1
2
3
4
5
include CarrierWave::RMagick
process :resize_to_fit => [800, 800]
version :thumb do
  process :resize_to_fill => [200,200]
end

Стоит знать, что :resize_to_fill и :resize_to_fit при одинаковом разрешении делает разные вещи, одно обрезает, другое масштабирует.

Подробнее о настройках читаем на офсайте, также можно посмотреть RailsCast.

Uploadify

Uploadify это набор скриптов, которые отсылают через AJAX файл, непосредственно к самим рельсам и руби оно не имеет никакого отношения.

установка

Качаем сам Uploadify и затем распаковываем. Теперь для Rails 3.1.x кидаем файлы jquery.uploadify.v2.1.4.min.js и swfobject.js в app/assets/javascripts, для Rails 3.0.x закидываем их в public/javascripts. Первый файл надо переименовать в jquery.uploadify.js.

Файлы uploadify.swf и cancel.png кидаем в app/assets/images/ или public/images, а uploadify.css в app/assets/stylesheets/ или public/stylesheets.

Открываем application.js из app/assets/javascripts или public/javascripts для разных версий Rails соответственно, и в конец дописываем:

//= require swfobject
//= require jquery.uploadify

В форму загрузки изображения (у меня app/views/pictures/_form.html.erb) закидываем сначала:

<input id="uploadify" name="uploadify" type="file" />

а после:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
  $(document).ready(function() {
  var uploadify_script_data = {};
  $('#uploadify').uploadify({
    uploader        : '/assets/uploadify.swf',
    script          : '<%=pictures_path%>',
    cancelImg       : '/images/cancel.png',
    auto            : true,
    multi           : true,
    removeCompleted : true,
    scriptData      : uploadify_script_data,
    onComplete      : function(event, ID, fileObj, doc, data){
    }
  });
});
</script>

В принимающем контроллере (здесь app/controllers/pictures_controller.rb) прописываем например:

1
2
3
4
def create
    @picture = Picture.new(:image => params[:Filedata])
    @picture.save
end

И в самом начале контроллера убираем защиту от CSRF:

1
protect_from_forgery :except => :create

Для тех кто не хочет отключать защиту от CSRF может попытаться следовать полной инструкции.

Если все правильно сделали, то должно получиться как на первой демке.

Дополнение от 14.04.2012 (касаемо CSRF). Архиважное.

Итак, чтобы не отключать защиту от CSRF нужно сделать следующее.

1. Бандлим гем flash_cookie_session, он нужен для того, чтобы не прописывать вагон всякой ерунды про поле User-Agent в заголовке запроса, если агентом является Flash, как в нашем случае.

1
2
echo "gem 'flash_cookie_session'" >> Gemfile
bundle install

2. Прописываем в дополнительные параметры Uploadify authenticity_token и кое-что еще, тем самым приводя инициализирующий скрипт к виду:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
$(document).ready(function() {
  $('#uploadify').uploadify({
    uploader        : '/assets/uploadify.swf',
    script          : '<%=uploadify_pictures_path%>',
    cancelImg       : '/assets/cancel.png',
    auto            : true,
    multi           : true,
    removeCompleted : true,
    buttonText      : 'Select Files',
    scriptData      : {
     '_http_accept': 'application/javascript',
     '_method': 'put',
     '<%=Rails.application.config.session_options[:key] %>':
     encodeURIComponent('<%= u cookies[session_key_name] %>'),
     'authenticity_token':
     encodeURIComponent('<%= u form_authenticity_token %>')
     }
  });
});
</script>

В результате этой мимикрии мы сможем оставить защиту от CSRF и сохранить сессию при запросе от Uploadify.

Источники дополнения:
http://www.damiangalarza.com/posts/ruby-on-rails/using-uploadify-with-rails-3/
http://erniemiller.org/2010/07/09/uploadify-and-rails-3/

2 Comments to “загрузка изображений AJAX’ом на Rails 3. CarrierWave+Uploadify”

  1. [...] также как и в случае с Uploadify нужно сгенерировать аплодер carrierwave: 1 $ rails g uploader [...]

  2. lukmus пишет:

    Важное дополнение!!! Если ответ в контроллере идет в JS (respond_to {|format| format.js}), то чтобы страница исполнила скрипт в JS-вьюхе нужно в инициализатор Uploadify вставить:
    onComplete : function(event, queueID, fileObj, response, data){eval(response);}

Leave a Reply

(обязательно)

(обязательно)