В первой части статьи мы написали REST приложение и на 1/3 настроили Redactor.js. Сегодня мы закончим наше изобретение, написав интерфейс управления загруженными изображениями, и обеспечим загрузку файлов. При загрузке файлов мы не будем использовать CarrierWave, а пойдем обычным путем Ruby.
Начнем с загрузки файлов. Предполагаю, что сейчас у вас такой файл init.rb:
Итак, создадим папку files в директории public/uploads, напишем новую модель UploadedFiles и добавим метод post для загрузки файлов
Теперь осталось сказать редактору как именно загружать файлы, откроем layout.erb найдем строку
Добавим настройку fileUpload: '/upload/file' чтобы выглядело следующим образом
Все необходимые настройки мы сделали: создали модель, настроили путь к загружаемым файлам, собственно реализовали саму загрузку файла, сделали проверку на разрешенные файлы. Вот что хотелось бы отметить, неплохо было бы реализовать сообщение об ошибке, если расширение файл не соответствует разрешенному, но тут есть 2 НО: во-первых, Redactor.js все делает во фрейме и, если сделать сообщение об ошибке, то оно будет писаться в textarea, что, на мой взгляд, не является хорошей идеей, если попытаться реализовать сообщение об ошибке через gem sinatra-flash, то опять не вариант, так как ошибка появится только после перегрузки страницы. Ну может более опытные люди подскажут в какую сторону копать.
Теперь приступим к управлению загруженными изображениями, максимум что мы сделаем, это список всех загруженных изображений и их удаление.
Добавим в init.rb
Создадим в директории views файл images.erb
Ну вот мы и закончили наше приложение. По аналогии с управлением изображений можно сделать и управление файлами. Думаю получилось неплохо. И это еще не конец, я продолжаю изучать Ruby и Sinatra и будут еще статьи.
Исходный код на github
P.S.: Везде где попадается
Начнем с загрузки файлов. Предполагаю, что сейчас у вас такой файл init.rb:
# coding: utf-8 require 'rubygems' require 'sinatra' require 'data_mapper' require 'carrierwave' require 'carrierwave/datamapper' require 'rmagick' require 'json' set :public_directory, './public' class ImageUploader < CarrierWave::Uploader::Base def store_dir 'uploads/images' end def extension_white_list %w(jpg jpeg gif png bmp) end include CarrierWave::RMagick version :thumb do process :resize_to_fill => [100,74] end storage :file end class Post include DataMapper::Resource property :id, Serial property :title, String property :body, Text end class UploadedImages include DataMapper::Resource property :id, Serial property :image, String property :thumb, String mount_uploader :file, ImageUploader end class UploadedFiles include DataMapper::Resource property :id, Serial property :name, String property :path, String end DataMapper.setup(:default, ENV['DATABASE_URL'] || 'sqlite:./db/base.db') DataMapper.finalize DataMapper.auto_upgrade! get '/' do 'REST приложение на Sinatra <a href="/posts">Перейти к страницам</a>' end #List posts get '/posts' do @posts = Post.all erb :'index' end #Create new Post get '/posts/new' do erb :'posts/new' end post '/posts/new' do params.delete 'submit' @post = Post.create(params) redirect '/posts' end #Edit post get '/posts/:id/edit' do @post = Post.get(params[:id]) erb :'posts/edit' end #Update post put '/posts/:id/edit' do post = Post.get(params[:id]) post.title = (params[:title]) post.body = (params[:body]) post.save redirect '/posts' end #Delete post get '/posts/:id/delete' do Post.get(params[:id]).destroy redirect '/posts' end post '/upload/image' do params[:file] filename = params[:file][:filename] file = params[:file][:tempfile] upload = UploadedImages.new upload.file = params[:file] upload.image = params[:image] = '/uploads/images/' + File.join(filename) upload.thumb = params[:thumb] = '/uploads/images/thumb_' + File.join(filename) upload.save @images = UploadedImages.all File.open("public/uploads/images/imageslist.json","w") do |f| f.write JSON.pretty_generate(@images) end '<img src="/uploads/images/' + File.join(filename) + '" />' end
Итак, создадим папку files в директории public/uploads, напишем новую модель UploadedFiles и добавим метод post для загрузки файлов
set :files, File.join(settings.public_directory, 'uploads/files') #берем из настройки public директории (set :public_directory, './public' почти в самом начале init.rb) ее путь и прибавляем путь, куда будут загружаться файлы class UploadedFiles include DataMapper::Resource property :id, Serial #идентификатор property :name, String #имя загруженного файла property :path, String #путь к загруженному файлу end post '/upload/file' do params[:file] filename = params[:file][:filename] file = params[:file][:tempfile] ext = File.extname(filename) #устанавливаем переменную для проверки расширения файла, нам же не нужно, чтобы пытались загружать что угодно. if ext == ".doc" || ext == ".zip" || ext == ".dmg" #здесь я установил 3 допустимых расширения файла doc, zip, dmg, при желании список разрешенных файлов всегда можно увеличить. File.open(File.join(settings.files, filename), 'wb') #загрузили - сохранили upload_f = UploadedFiles.new #создаем новый UploadedFile в базе данных upload_f.name = params[:name] = File.join(filename) #в моделе UploadedFiles поле :name, записываем туда имя загруженного файла upload_f.path = params[:path] = '/uploads/files/' + File.join(filename) #в моделе UploadedFiles поле :path, записываем туда путь к загруженному файлу upload_f.save #сохраняем '<a href="/uploads/files/' + File.join(filename) + '" />' + File.join(filename) + '</a>' #вставляем в редактор ссылку на файл end end
Теперь осталось сказать редактору как именно загружать файлы, откроем layout.erb найдем строку
$('.redactor_1').redactor({toolbar: 'default', lang: 'ru', imageUpload: '/upload/image', imageGetJson: '/uploads/images/imageslist.json'});
Добавим настройку fileUpload: '/upload/file' чтобы выглядело следующим образом
$('.redactor_1').redactor({toolbar: 'default', lang: 'ru', imageUpload: '/upload/image', fileUpload: '/upload/file', imageGetJson: '/uploads/images/imageslist.json'});
Все необходимые настройки мы сделали: создали модель, настроили путь к загружаемым файлам, собственно реализовали саму загрузку файла, сделали проверку на разрешенные файлы. Вот что хотелось бы отметить, неплохо было бы реализовать сообщение об ошибке, если расширение файл не соответствует разрешенному, но тут есть 2 НО: во-первых, Redactor.js все делает во фрейме и, если сделать сообщение об ошибке, то оно будет писаться в textarea, что, на мой взгляд, не является хорошей идеей, если попытаться реализовать сообщение об ошибке через gem sinatra-flash, то опять не вариант, так как ошибка появится только после перегрузки страницы. Ну может более опытные люди подскажут в какую сторону копать.
Теперь приступим к управлению загруженными изображениями, максимум что мы сделаем, это список всех загруженных изображений и их удаление.
Добавим в init.rb
#init.rb #List UploadedImages get '/images' do @ uploadedimage = UploadedImages.all #задаем переменную для вывода всех загруженных изображений по адресу /images (убрать пробел между собакой и uploadedimage) erb :'images' #рендерим images.erb end #Delete UploadedImage get '/images/:id/delete' do #обращаемся к изображению через его ID UploadedImages.get(params[:id]).destroy #удаляем (честно говоря, для меня какое-то волшебство, удаляет строки в бд и файлы!...) redirect '/images' #редеректимся на страницу со всеми оставшимися изображениями end
Создадим в директории views файл images.erb
<strong><a href="/posts">Перейти ко всем post'ам</a></strong> <h2>Список загруженных файлов</h2> <% @ uploadedimage.each do |uploadedimages| %> <a href="<%= uploadedimages.image %>"><img src="<%= uploadedimages.thumb %>"></a><br> #ссылка на полное изображение при клике на его превью <a href="/images/<%= uploadedimages.id %>/delete">удалить</a><br><br> <% end %>
Ну вот мы и закончили наше приложение. По аналогии с управлением изображений можно сделать и управление файлами. Думаю получилось неплохо. И это еще не конец, я продолжаю изучать Ruby и Sinatra и будут еще статьи.
Исходный код на github
P.S.: Везде где попадается
или<hh user=posts>
просто замените на @название_переменной<hh user=images>