Right now I am very happy! I finished my work on creating a file upload method in rails for more than one file, altogether handled by rails sessions and as objects.
First I had to learn a bit more about how rails works and I achieved that by reading a lot: books, screencasts and blogs.
In this blog post I like to share what I accomplished, but without the hussle of putting code blocks in here, so watch out for bold formatted text as that is the code.
My goal was to have one site with just the pure option of uploading files.
All file handling would be done behind the scenes by myself logging onto my server. That said you are not going to see any fancy CRUD here.
Lets start then!
And dont forget to versioning your app BEFORE you try something new!
[e.g. git]
> rails g scaffold Photo title:string
Now add carrierwave to you Gemfile and run
> bundle install
>rails g uploader Photo
Alltogether you have model, controller, views and an uploader folder with the PhotoUploader; and run:
> rake db:migrate
And dont forget to download the plupload files and putting them into the right folders, app/assets/.., and test the whole thing first with the plupload custom.html file you will find in the archive downloaded from the official site;
I just used that sample code e.g.
Now we take a look at our model:
# models/photo.rb
class Photo < ActiveRecord::Base
attr_accessible :title, :title_cache # access?!
mount_uploader :title, PhotoUploader # on what column? title!
end
These option differ on what storage, filetypes etc you want:
#uploaders/photo_uploader.rb
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}" # its in "root"/public/..
end
def extension_white_list
%w(jpg jpeg png zip rar) # adjust this also in plupload!
end
#the next method is for big files not to be copied but moved
def move_to_store
true
end
Well, carrierwave is installed now and will put files on your disk.
We just have to tell rails to work with carrierwave:
(here my full photos_controller.rb with BREAKS!)
# controller/photos_controller.rb
class PhotosController < ApplicationController
# GET /photos
# GET /photos.json
def index
@photos = Photo.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @photos }
end
end
# GET /photos/1
# GET /photos/1.json
def show
@photo = Photo.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @photo }
end
end
#BREAK:
#The next part took me a while to understand. The browser sends a GET new #and gets a form to fill out. Thats why the object is created here:
# GET /photos/new
# GET /photos/new.json
def new
@photo = Photo.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: @photo }
end
end
# GET /photos/1/edit
def edit
@photo = Photo.find(params[:id])
end
# BREAK:
# After the user has filled out the form its send automaticly to the browser
# and invokes "create". The first and commented block shows just carrierwave
# dealing with the parameter in the url and fetching the right one
# to save the data; also a flash message is printed out.
# POST /photos
# POST /photos.json
def create
## carrierwave as single
# @photo = Photo.new(params[:photo])
#
# if @photo.save
# flash[:notice] = "Successfully created photo."
# redirect_to @photo
# else
# render :action => 'new'
#end
# BREAK:
# when you mastered carrierwave on its own you should get plupload
# get to work with the following lines.
# IMPORTANT is that plupload has a different url parameter, but see:
# and in this case we just leave the user on the page
@photo = Photo.new(:title => params[:file])
@photo.save!
render :nothing => true
end
# PUT /photos/1
# PUT /photos/1.json
def update
@photo = Photo.find(params[:id])
respond_to do |format|
if @photo.update_attributes(params[:photo])
format.html { redirect_to @photo, notice: 'Photo was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @photo.errors, status: :unprocessable_entity }
end
end
end
# DELETE /photos/1
# DELETE /photos/1.json
def destroy
@photo = Photo.find(params[:id])
@photo.destroy
respond_to do |format|
format.html { redirect_to photos_url }
format.json { head :no_content }
end
end
end
Rails took care of all the other files and for carrierwave you just have to tell the form helper what to do for whom:
# views/photos/_form.html.erb
<%= form_for @photo, :html => {:multipart => true} do |f| %>
<% if @photo.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2>
<ul>
<% @photo.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<p>
<label>Photo:</label>
<%= f.file_field :title %>
<%= f.hidden_field :title_cache %>
</p>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In the views/photos/new.html.erb I put the code for plupload:
# views/new.html.erb
<div id="container">
<div id="filelist">Photos</div>
<br />
<a id="pickfiles" href="javascript:;">[Select files]</a>
<a id="uploadfiles" href="javascript:;">[Upload files]</a>
</div>
<script type="text/javascript">
// Custom example logic
function $(id) {
return document.getElementById(id);
}
var uploader = new plupload.Uploader({
runtimes : 'gears,html5,flash,silverlight,browserplus',
browse_button : 'pickfiles',
container: 'container',
url : '/photos',
multipart: true,
multipart_params: {
"authenticity_token" : '<%= form_authenticity_token %>'
},
filters : [
{title : "Image files", extensions : "jpg,jpeg,png"},
{title : "Zip files", extensions : "zip, rar"}
]
});
[...] ATTENTION: for the rest look at the custom.html of plupload
That should have done the trick of letting rails handle multiple file uploads.
And if you want to see those files, e.g. pictures:
<%= image_tag(@photo.title_url) if @photo.title? %>
The whole process took me some hours, quite a lot actually,
but it was a good practise getting to know rails too.
Have fun!
PS: try to move the js out of you application.html.erb by putting your "special" js files in subfolders.
# application.html.erb
<%= yield :javascript_includes %>
# new.html.erb
<% content_for :javascript_includes do %>
<%= javascript_include_tag "plupload/plupload.full.js" %>
<%= javascript_include_tag "plupload/plupload.queue.js" %>
<%= javascript_include_tag "plupload/jquery.ui.plupload.js" %>
<% end %>
PPS: to go through all the uploads I made the folders more specific:
def store_dir
d = Date.today
"uploads/#{model.class.to_s.underscore}/#{d.to_s(:db)}-#{d.strftime("%A")}"
end
PPPS: tell capistrano to let the upload folder alone!
how-to-get-capistrano-to-ignore-upload-directories-carrierwave
First I had to learn a bit more about how rails works and I achieved that by reading a lot: books, screencasts and blogs.
In this blog post I like to share what I accomplished, but without the hussle of putting code blocks in here, so watch out for bold formatted text as that is the code.
My goal was to have one site with just the pure option of uploading files.
All file handling would be done behind the scenes by myself logging onto my server. That said you are not going to see any fancy CRUD here.
Lets start then!
And dont forget to versioning your app BEFORE you try something new!
[e.g. git]
> rails g scaffold Photo title:string
Now add carrierwave to you Gemfile and run
> bundle install
>rails g uploader Photo
Alltogether you have model, controller, views and an uploader folder with the PhotoUploader; and run:
> rake db:migrate
And dont forget to download the plupload files and putting them into the right folders, app/assets/.., and test the whole thing first with the plupload custom.html file you will find in the archive downloaded from the official site;
I just used that sample code e.g.
Now we take a look at our model:
# models/photo.rb
class Photo < ActiveRecord::Base
attr_accessible :title, :title_cache # access?!
mount_uploader :title, PhotoUploader # on what column? title!
end
These option differ on what storage, filetypes etc you want:
#uploaders/photo_uploader.rb
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}" # its in "root"/public/..
end
def extension_white_list
%w(jpg jpeg png zip rar) # adjust this also in plupload!
end
#the next method is for big files not to be copied but moved
def move_to_store
true
end
Well, carrierwave is installed now and will put files on your disk.
We just have to tell rails to work with carrierwave:
(here my full photos_controller.rb with BREAKS!)
# controller/photos_controller.rb
class PhotosController < ApplicationController
# GET /photos
# GET /photos.json
def index
@photos = Photo.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @photos }
end
end
# GET /photos/1
# GET /photos/1.json
def show
@photo = Photo.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @photo }
end
end
#BREAK:
#The next part took me a while to understand. The browser sends a GET new #and gets a form to fill out. Thats why the object is created here:
# GET /photos/new
# GET /photos/new.json
def new
@photo = Photo.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: @photo }
end
end
# GET /photos/1/edit
def edit
@photo = Photo.find(params[:id])
end
# BREAK:
# After the user has filled out the form its send automaticly to the browser
# and invokes "create". The first and commented block shows just carrierwave
# dealing with the parameter in the url and fetching the right one
# to save the data; also a flash message is printed out.
# POST /photos
# POST /photos.json
def create
## carrierwave as single
# @photo = Photo.new(params[:photo])
#
# if @photo.save
# flash[:notice] = "Successfully created photo."
# redirect_to @photo
# else
# render :action => 'new'
#end
# BREAK:
# when you mastered carrierwave on its own you should get plupload
# get to work with the following lines.
# IMPORTANT is that plupload has a different url parameter, but see:
# and in this case we just leave the user on the page
@photo = Photo.new(:title => params[:file])
@photo.save!
render :nothing => true
end
# PUT /photos/1
# PUT /photos/1.json
def update
@photo = Photo.find(params[:id])
respond_to do |format|
if @photo.update_attributes(params[:photo])
format.html { redirect_to @photo, notice: 'Photo was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @photo.errors, status: :unprocessable_entity }
end
end
end
# DELETE /photos/1
# DELETE /photos/1.json
def destroy
@photo = Photo.find(params[:id])
@photo.destroy
respond_to do |format|
format.html { redirect_to photos_url }
format.json { head :no_content }
end
end
end
Rails took care of all the other files and for carrierwave you just have to tell the form helper what to do for whom:
# views/photos/_form.html.erb
<%= form_for @photo, :html => {:multipart => true} do |f| %>
<% if @photo.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2>
<ul>
<% @photo.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<p>
<label>Photo:</label>
<%= f.file_field :title %>
<%= f.hidden_field :title_cache %>
</p>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In the views/photos/new.html.erb I put the code for plupload:
# views/new.html.erb
<div id="container">
<div id="filelist">Photos</div>
<br />
<a id="pickfiles" href="javascript:;">[Select files]</a>
<a id="uploadfiles" href="javascript:;">[Upload files]</a>
</div>
<script type="text/javascript">
// Custom example logic
function $(id) {
return document.getElementById(id);
}
var uploader = new plupload.Uploader({
runtimes : 'gears,html5,flash,silverlight,browserplus',
browse_button : 'pickfiles',
container: 'container',
url : '/photos',
multipart: true,
multipart_params: {
"authenticity_token" : '<%= form_authenticity_token %>'
},
filters : [
{title : "Image files", extensions : "jpg,jpeg,png"},
{title : "Zip files", extensions : "zip, rar"}
]
});
[...] ATTENTION: for the rest look at the custom.html of plupload
That should have done the trick of letting rails handle multiple file uploads.
And if you want to see those files, e.g. pictures:
<%= image_tag(@photo.title_url) if @photo.title? %>
The whole process took me some hours, quite a lot actually,
but it was a good practise getting to know rails too.
Have fun!
PS: try to move the js out of you application.html.erb by putting your "special" js files in subfolders.
# application.html.erb
<%= yield :javascript_includes %>
# new.html.erb
<% content_for :javascript_includes do %>
<%= javascript_include_tag "plupload/plupload.full.js" %>
<%= javascript_include_tag "plupload/plupload.queue.js" %>
<%= javascript_include_tag "plupload/jquery.ui.plupload.js" %>
<% end %>
PPS: to go through all the uploads I made the folders more specific:
def store_dir
d = Date.today
"uploads/#{model.class.to_s.underscore}/#{d.to_s(:db)}-#{d.strftime("%A")}"
end
PPPS: tell capistrano to let the upload folder alone!
how-to-get-capistrano-to-ignore-upload-directories-carrierwave
Kommentare
Kommentar veröffentlichen