В этом уроке я собираюсь показать вам, как использовать Ruby on Rails 4.2 и Dropzone для загрузки нескольких файлов с помощью перетаскивания с помощью AJAX. Dropzone — это потрясающая библиотека, которая, по мнению автора, стоит на голову выше остальных библиотек, предлагающих аналогичную функциональность.
Обратите внимание, что я собираюсь использовать обычный Ruby on Rails без какой-либо другой библиотеки или гемов, таких как Paperclip или Carrierwave.
Этот учебник подходит для тех, кто приходит с других языков программирования и хочет изучить Rails.
ОК, ребята, давайте отправимся в путь.
Предпосылки
Я предполагаю, что:
- Вы немного знаете об объектно-ориентированном программировании и о том, как три компонента шаблона проектирования MVC связаны друг с другом.
- Вы знакомы с запросами javascript, jQuery и AJAX.
Инструменты нам понадобятся
- Хорошие инструменты для отладки и анализа браузера. Я использую Firefox в качестве браузера с установленным Firebug для отладки и управления взаимодействиями между JavaScript в браузере и Rails на сервере.
- Любая IDE или редактор. Мне нравится Rubymine или Sublime, но не стесняйтесь использовать свои собственные.
- Ruby on Rails 4.2. Чтобы установить Rails, посетите (http://installrails.com/). Я собираюсь использовать SQLite в качестве базы данных. Вы также можете использовать PostgreSQL, MySQL или любую другую СУБД.
- Сама библиотека Dropzone. Иди и скачай его отсюда .
- Twitter Bootstrap .
Приложение
Наше приложение представляет собой модуль интернет-магазина для создания товаров и прикрепления к ним изображений. Он загружает изображения на сервер, возвращает список загруженных файлов, а затем отправляет форму для создания записи о продукте. Между изображениями и продуктом существует связь «один ко многим», что означает, что продукт может иметь много изображений.
Вот схема базы данных:
Давайте перейдем к нашему коду. Сначала создайте новое приложение Rails, открыв свой терминал и напечатав:
rails new myapp
Нам нужно добавить файлы Javascript и CSS для Dropzone и Bootstrap, поэтому перейдите в папку dist из загрузок Dropzone и Bootstrap и найдите следующие файлы:
bootstrap.css dropzone.css basic.css dropzone.js
Поместите dropzone.js в myapp / app / assets / javascripts и все CSS-файлы в myapp / app / assests / stylesheets . Откройте application.css и удалите эту строку:
*= require_tree .
Эта строка сообщает Rails, что требуется все в этом каталоге и во всех его подкаталогах. Мы не хотим, чтобы это произошло, поэтому избавьтесь от этого. Затем добавьте эту строку:
*= require bootstrap
Что означает включить загрузчик для нашего приложения. Закройте и сохраните файл.
Перейдите в myapp / app / assets / javascripts , откройте application.js и удалите следующие строки:
//= require turbolinks //= require_tree .
Для получения дополнительной информации о //= require turbolinks
пожалуйста, прочитайте эту статью . Мы не собираемся использовать Turbolinks сегодня. Сохраните и закройте файл. Теперь перейдите в app / views / layouts / application.html.erb и создайте шаблон макета для вашего приложения. Ваш окончательный файл должен выглядеть так:
<!DOCTYPE html> <html> <head> <title>Myapp</title> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> <%= yield(:css) %> <!-- We use this section for our CSS files --> </head> <body> <%= yield(:content) %><!-- For our main contents --> <%= yield(:javascript) %><!-- We put our JavaScript tags here --> </body> </html>
Обратите внимание, что мы получили три раздела: css
, content
и javascript
. Откройте терминал и введите следующую команду:
rails g controller Product new create
Это создаст два представления в вашей папке app / views и контроллер в app / controllers . Прежде чем двигаться дальше, перейдите в myapp / config / initializers / assets.rb и добавьте эту строку в файл:
Rails.application.config.assets.precompile += %w(dropzone.js basic.css dropzone.css)
чтобы убедиться, что наши сторонние ресурсы предварительно скомпилированы с остальной частью приложения.
Теперь я собираюсь написать на стороне клиента, прежде чем перейти к реализации на стороне сервера. Откройте app / views / new.html.erb и введите следующий код:
Этот раздел включает в себя файлы CSS, basic.css и dropzone.css, которые будут использоваться для нашей области перетаскивания:
<%= content_for(:css) do %> <%= stylesheet_link_tag 'dropzone' %> <%= stylesheet_link_tag 'basic' %> <% end %>
Здесь мы включаем форму для создания продукта и зону перетаскивания, которая позволяет пользователю перетаскивать изображения:
<%=content_for(:content) do %> <div class="container"> <div class="row"> <p>This is the form for creation of your product.</p> <%= form_for :request, :url => request.base_url+'/product/create', html: {id:'myForm'} do %> <label for="name">Product Name:</label> <input type="text" name="name" id="name" class="form-control"> <label for description> Description</label> <input type="text" name="description" id="description" class="form-control" /><br> <input type=hidden name="files_list" id='fileslist'> <!-- We use this <div> element to initialize our Dropzone --> <div id="mydropzone" class="dropzone"></div> <!-- This <div> elements shows a suitable message after a successful upload. --> <div id="msgBoard"></div> <br> <input type='submit' value="Create your product"> </div> </div> <% end %> <% end %> <%= content_for(:javascript) do %> <!-- include the dropzone library itself. --> <%= javascript_include_tag "dropzone" %>
Вот Javascript, необходимый для обработки событий перетаскивания и загрузки формы. Я объясню каждую часть кода при прохождении через него:
<script type="text/javascript"> var AUTH_TOKEN=$('meta[name="csrf-token"]').attr('content');
Возьмите токен CSRF с помощью jQuery и сохраните его в переменной AUTH_TOKEN
:
Dropzone.autoDiscover = false; var myDropzone = new Dropzone("div#mydropzone",{ url: "<%= request.base_url %>/uploadfiles", autoProcessQueue: false, uploadMultiple: true, addRemoveLinks:true, parallelUploads:10, params:{ 'authenticity_token': AUTH_TOKEN }, successmultiple: function(data,response){ $('#msgBoard').append(response.message).addClass("alert alert-success"); $('#msgBoard').delay(2000).fadeOut(); $('#fileslist').val(response.filesList); $('#myForm').off('submit').submit(); } });
Инициализируйте наш перетаскиватель файлов:
-
url
— маршрут, который обрабатывает задачу загрузки изображений на сервер -
autoProcessQueue: false
останавливает скрипт для автоматической загрузки изображений после их перетаскивания -
uploadMultiple: true
позволяетuploadMultiple: true
отправлять несколько файлов за один запрос -
addRemoveLinks: true
Если установлено значение true, ссылка «Удалить» будет добавлена для каждого предварительного просмотра файла. -
parallelUploads: 10
устанавливает количество параллельных загрузок, разрешенное до 10 -
params
позволяет нам предоставлять дополнительные данные вместе с запросом. Здесь нам нужно передать токен CSRF. Прежде чем двигаться дальше, давайте обсудим некоторые детали отправки AJAX-запросов в приложении Rails. Как вы, вероятно, знаете, если вы не включите токен CSRF в запросы, отправленные в Rails, вы получите эту ошибку в консоли Firebug:ActionController::InvalidAuthenticityToken in ProductController#upload
На самом деле это выглядит так:
Итак, мы передаем токен CSRF здесь.
-
successmultiple: function(data,response)
: если процесс загрузки на сервере выполнен успешно, мы обрабатываем данные и ответ с помощью этой функции обратного вызова. Эта функция добавляет сообщение в нашmsgBoard
, затемmsgBoard
это сообщение, добавляет загруженные файлы в скрытый ввод в форме, а затем отправляет форму.
После отправки формы этот сценарий проверяет, является ли список файлов в области рабочей зоны пустым. Если есть какие-либо файлы для загрузки, он отправляет запрос POST на сервер и загружает их ( myDropzone.processQueue()
).
$('#myForm').submit(function(e){ if(myDropzone.getQueuedFiles().length > 0){ e.preventDefault(); myDropzone.processQueue(); } }); </script> <% end %>
В этом приложении Rails нам нужно в первую очередь запретить отправку формы, пока мы не убедимся, что все файлы в зоне размещения загружены. Как только все файлы были загружены, мы можем отправить форму. Поэтому в функции обратного вызова для successmultiple
мы позволяем событию submit происходить методом off
из jQuery.
Следующая строка отправляет форму, если скрипт успешно загружает наши изображения и сервер отправляет сообщение об успешном выполнении с сервера:
`$('#myForm').off('submit').submit();`
Теперь нам нужно реализовать серверную часть нашего приложения, которая отправляет сообщение об успешном завершении вместе со списком загруженных файлов. Откройте файл config / rout.rb и добавьте следующую строку:
post 'uploadfiles'=>'product#upload'
Затем перейдите в myapp / public и создайте каталог загрузки с помощью следующей команды:
mkdir uploads
Это каталог, который будет содержать наши загруженные файлы.
В app / controller / product_controller.rb определите метод загрузки следующим образом:
def upload uploaded_pics = params[:file] # Take the files which are sent by HTTP POST request. time_footprint = Time.now.to_i.to_formatted_s(:number) # Generate a unique number to rename the files to prevent duplication uploaded_pics.each do |pic| # these two following comments are some useful methods to debug # abort pic.class.inspect -> It is similar to var_dump($variable) in PHP. # abort pic.is_a?(Array).inspect -> With "is_a?" method, you can find the type of variable # abort pic[1].original_filename.inspect # The following snippet saves the uploaded content in '#{Rails.root}/public/uploads' with a name which contains a time footprint + the original file # reference: http://guides.rubyonrails.org/form_helpers.html File.open(Rails.root.join('public', 'uploads', pic[1].original_filename), 'wb') do |file| file.write(pic[1].read) File.rename(file, 'public/uploads/' + time_footprint + pic[1].original_filename) end end files_list = Dir['public/uploads/*'].to_json #get a list of all files in the {public/uploads} directory and make a JSON to pass to the server render json: { message: 'You have successfully uploded your images.', files_list: files_list } #return a JSON object amd success message if uploading is successful end
На стороне сервера мы берем файлы, отправленные через POST-запрос, с хэшем params . Затем сгенерируйте уникальную числовую строку, которая будет использоваться для создания уникальных имен файлов. Тем самым мы предотвращаем дублирование.
Затем мы загружаем и переименовываем файлы один за другим. Наконец, получите список всех загруженных файлов и верните JSON с сообщением об успешном завершении и список всех успешно загруженных файлов.
Хорошо, пришло время запустить наше приложение и посмотреть, что мы уже сделали. Перейдите в папку myapp и запустите сервер:
rails s
В браузере перейдите на страницу нового продукта . Вы должны увидеть следующую форму:
Если вы отправите форму прямо сейчас, вы загрузите файлы в каталог public / uploads . Нам нужно создать продукт и связать эти изображения с этим продуктом. Давайте создадим наши модели и их файлы миграции.
Сначала создайте модель и миграцию для нашего продукта . Продукт имеет название и описание, а также идентификатор. Откройте свой терминал и введите:
rails generate model Product name:string description:text
Во-вторых, создайте модель pic и ее файл миграции, введя следующую команду:
rails generate model Pic product_id:integer:index name:text
Перейдите в папку myapp / db / migrate и найдите файл миграции для модели Pic . Откройте его и добавьте внешний ключ с этим фрагментом кода:
add_foreign_key :pics, :products
Функция change
вашего файла миграции должна выглядеть следующим образом:
def change create_table :pics do |t| t.integer :product_id, index: true t.text :name t.timestamps null: false end add_foreign_key :pics, :products end
Время установить связь между моделями Product
и Pic
. Эта задача невероятно проста в приложениях Rails. В реальном мире, мы бы сказали, что каждый продукт имеет много изображений, и каждая картинка принадлежит продукту. Итак, откройте модель вашего Product
и добавьте этот фрагмент кода:
has_many :pics
Ваша модель теперь выглядит так:
class Product < ActiveRecord::Base has_many :pics end
Поскольку каждое изображение принадлежит продукту, вам необходимо добавить эту строку в модель Pic
:
belongs_to :product
Вот и все. Время мигрировать с rake
:
rake db:migrate
Что касается заключительной части, приложение должно создать продукт после загрузки файлов и связать этот продукт с изображениями, которые уже загружены нашей зоной размещения. Эта задача будет обработана методом create
в ProductController
. Откройте приложение / controllers / product_controller.rb .
Метод create
должен создать продукт для нас, установить связь «один ко многим» и переместить загруженный файл в папку, специально созданную для данного продукта. Имя папки — это идентификатор продукта. Кроме того, метод должен создать флэш-сообщение, если операция прошла успешно, и перенаправить пользователя на предыдущую страницу:
def create files_list = ActiveSupport::JSON.decode(params[:files_list]) product=Product.create(name: params[:name], description: params[:description]) Dir.mkdir("#{Rails.root}/public/"+product.id.to_s) files_list.each do |pic| File.rename( "#{Rails.root}/"+pic, "#{Rails.root}/public/"+product.id.to_s+'/'+File.basename(pic)) product.pics.create(name: pic) end redirect_to product_new_url, notice: "Success! Product is created." end
Флэш-сообщение будет отображаться в верхней части страницы создания, поэтому нам нужно создать элемент div
который покажет пользователю сообщение о создании. Добавьте следующий код в файл app / views / product / new.html.erb .
<% if flash.notice %> <div class="alert alert-success"><P><%= flash.notice %></p></div> <% end %>
ОК, мы закончили с этим небольшим модулем для нашего приложения электронной коммерции. Запустите сервер rails ( rails s
) и перейдите на страницу нового продукта в браузере. Если вы заполните форму, перетащите ее изображения и нажмете кнопку «Отправить», тогда приложение загрузит изображения, создаст наш продукт и свяжет загруженные изображения с нашим продуктом.
Например, я собираюсь создать велосипедный продукт и загрузить три его изображения вместе с названием и описанием. Вот как выглядит страница нового продукта :
Если я нажму кнопку «Создать свой продукт», она создаст запись и перенаправит меня на ту же страницу с флэш-сообщением следующим образом:
Вывод
Хорошо, это все, что нужно для создания этого небольшого модуля для нашего интернет-магазина. Dropzone.js позволил нам снабдить наше Rails-приложение приятным пользовательским интерфейсом для загрузки изображений без необходимости использовать файл для загрузки файлов.
Я надеюсь, что вы нашли это полезным.