Upload souboru v Ruby on Rails
Včera v noci jsem si trochu pohrával s frameworkem Ruby on Rails. Je to vlastně první pidiaplikace, kterou jsem v railsech vytvořil a musím říct, že po celodenní píííp s píííp Java „Enterprise“ frameworky, servery a dalšími píííp to bylo příjemné programování.
Takže… nebudu popisovat jak si vytvořit kostru projektu, vytvoření databáze, nastartování Mongrelu… podobných návodů jsou stovky a navíc použití nějakého IDE vám tyhle věci usnadní. Já pořád ještě nevím, jestli je lepší Aptana (Eclipse + RadRails plugin) a nebo NetBeans, ale včera to celé vzniklo v beta verzi NetBeans 6.5.
Co to vlatně umí? Nic moc, umí to načíst soubor (obrázek), uložit do databáze, zase ho z té databáze načíst, zmenšit (změnit velikost) a zobrazit. Je to jenom minimální varianta, takže se zde nekontrolují vstupní data ani nic jiného, prostě jenom ukázka toho, jak to celé funguje.
Předpokládám, že máte nastavenou databázi (já použil MySQL) a pomocí gem nainstalované railsy a také rmagick, což je Ruby rozhraní pro ImageMagick.
Pro vytvoření modelu můžeme využít migrace. A ty je zase pro změnu dobré si nechat vygenerovat, takže:
$ script/generate migration create_image
To vygeneruje jednak model, tak také migraci. Do té stačí doplnit jediný řádek - položku pro uložení dat souboru. Výsledek bude vypadat nějak takto:
class CreateImages < ActiveRecord::Migration
def self.up
create_table :images do |t|
t.binary :data, :limit => 16_777_215
t.timestamps
end
end
def self.down
drop_table :images
end
end
Pomocí příkazu rake db:migrate pak vytvoříme tabulku v databázi. Další krok bude vytvoření controleru. Ten bude mít tři metody - index, ta bude sloužit pro zobrazení formuláře pro upload souboru, metoda upload bude zajišťovat uložení načteného souboru do databáze a následě ho zobrazí (do view vrstvy předá id obrázku) a metoda image, která podle id najde obrázek v databázi, načte ho, změní velikost a pošle na výstup. Všimněte si jak málo kódu stačí (všímají si zejména javisté, programátoři v „plain PHP“ si všímají naopak použití relačního mapování, oddělení view vrstvy apod. ;-).
require 'RMagick'
class UploadController < ApplicationController
def index
end
def upload
im = Image.new
im.data = params[:upload]['image'].read
im.save
@id = im.id
end
def image
image = Image.find(params[:id])
im = Magick::Image.from_blob(image.data).first;
res_im = im.resize(200,100);
send_data(res_im.to_blob,
:type => "image/jpg",
:filename => "jmenosouboru",
:disposition => 'inline')
end
end
Asi na tom není co vysvětlovat, všechno je krásně čitelné. Pro jistotu připomínám, že objekt Image se vygeneroval automaticky během migrace. Poslední co chybí, je view vrstva, budeme potřebovat soubor pro zobrazení formuláře index.rhtml
<h1>File Upload</h1>
<% form_tag({:action => 'upload'}, :multipart => true) do %>
<%= file_field 'upload', 'image' %>
<%= submit_tag "Upload" %>
<% end %>
a soubor upload.rhtml pro zobrazení výledku.
<%= image_tag("/upload/image/#{@id}", :alt => "Image") %>
A to je všechno. Ze začátku to působí trochu jako magie, ale není to tak hrozné. S čím se nějak nemůžu sžít je styl šablon, dal bych přednost kombinaci HTML a vlastních tagů, ale třeba něco podobného existuje. Uvedený příklad je moje řešení daného problému, uvítám, když v něm někdo najde chybu a upozorní mě na ni.