ファイルのアップロードとダウンロード
Windows環境でのファイルのアップロードやダウンロードを行うプログラムを紹介します。
1-1.プロジェクトの生成
(1) プロジェクトAppli018を生成する
(2) 日本語環境の設定
(3) データベースの作成
1-2.モデルの作成
(1) モデルの生成
NetBeansで生成を選択します。
ジェネレータ(G): model
引数(A): Folder
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/folder.rb
create test/unit/folder_test.rb
create test/fixtures/folders.yml
create db/migrate
create db/migrate/20100217040348_create_folders.rb
(2) マイグレーションのプログラム修正
class CreateFolders < ActiveRecord::Migration
def self.up
create_table :folders do |t|
t.string :filename
t.string :content_type
t.integer :sizet.timestamps
end
enddef self.down
drop_table :folders
end
end
(3) マイグレーションの実行
NetBeansで[データベースマイグレーション]→[現在のバージョンへ]を選択します。
(in D:/Rails_Projects/Appli018)
== CreateFolders: migrating ==================================================
-- create_table(:folders)
-> 0.1100s
== CreateFolders: migrated (0.1100s) =========================================
(4) モデルの編集
class Folder < ActiveRecord::Base
MAX_FILE_SIZE = 1.megabytevalidates_presence_of :filename, :message => 'Please choose a file.'
validates_inclusion_of :size, :in => (1..MAX_FILE_SIZE),
:message => "File size is too big or 0. Maximum size is #{MAX_FILE_SIZE} bytes."def file=(file)
self.filename = file.original_filename if file.respond_to?(:original_filename)
self.content_type = file.content_type if file.respond_to?(:content_type)
self.size = file.size if file.respond_to?(:size)
@tmp = file
enddef after_save
File.open(filepath, "wb") { |f|
f.write @tmp.read
}
end
def after_destroy
File.delete filepath if File.exist? filepath
end
def filepath
"public/files/#{self.id}_#{self.filename}"
end
end
1-3.コントローラの作成
(1) コントローラの生成
NetBeansで[生成]を選択します。
ジェネレータ(G): controller
名前(N): folders
ビュー(V): index, new, create, destroy, download
exists app/controllers/
exists app/helpers/
create app/views/folder
exists test/functional/
create test/unit/helpers/
create app/controllers/folders_controller.rb
create test/functional/folders_controller_test.rb
create app/helpers/folders_helper.rb
create test/unit/helpers/folders_helper_test.rb
create app/views/folders/index.html.erb
create app/views/folders/new.html.erb
create app/views/folders/create.html.erb
create app/views/folders/destroy.html.erb
create app/views/folders/download.html.erb
(2) コントローラの編集
class FoldersController < ApplicationController
def index
@folders = Folder.find(:all)
enddef new
end
def create
@folder = Folder.new(params[:upload])
if @folder.valid? then
@folder.save
redirect_to :action => 'index'
end
enddef destroy
@folder = Folder.find(params[:id])
@folder.destroy
redirect_to :action => 'index'
enddef download
@folder = Folder.find(params[:id])
send_file(@folder.filepath,
{:filename => @folder.filename,
:type => @folder.content_type})
end
end
1-4.ビューの編集
(1) index.html.erb
<h1>Index</h1>
<table>
<tr>
<th>filename</th>
<th>content_type</th>
<th>size</th>
<th>action</th>
</tr><% @folders.each do |folder| %>
<tr>
<td><%= link_to(h(folder.filename),
{:action => 'download',
:id => folder.id}) %>
</td>
<td><%= h(folder.content_type) %></td>
<td><%= h(folder.size) %></td>
<td>
<%= link_to('[destroy]',
{:action => 'destroy',
:id => folder.id}) %>
</td>
</tr><% end %>
</table><%= link_to('new', {:action => 'new'}) %>
(2) new.html.erb
<h1>New</h1><% form_tag({:action => 'create'}, :multipart => true) do %>
<%= file_field('upload', 'file') %>
<%= submit_tag('upload') %><% end %><%= link_to('index', {:action => 'index'}) %>
(3) create.html.erb
<h1>Create</h1><%= error_messages_for :folder %><%= link_to('index', {:action => 'index'}) %>
(4) 不用なビュープログラムの削除
/app/views/folders/destroy.html.erb
/app/views/folders/download.html.erb
(5) レイアウトファイルの作成
HTMLのヘッダー部分など各ページ共通に利用される表示要素を作成します。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Appli018: <%= controller.action_name %></title>
</head>
<body><%= yield %></body>
</html>
1-5.ファイルの格納先の作成
/public/の直下にfilesディレクトリーを作ります。
(1) filesディレクトリーの作成
公開ディレクトリー上で右クリックして[新規]→[フォルダ...]を選択します。
(3) ファイルの格納先
/public/files/[id]_[original_filename]
1-6.動作確認
http://127,0,0,1:3000/folders/で起動します。
1-7.表示の改善
(1) 数値のカンマ編集
サイズの表示方法をカンマ編集し見やすくしましょう。
<h1>Index</h1>
<table>
<tr>
<th>filename</th>
<th>content_type</th>
<th>size</th>
<th>action</th>
</tr><% @folders.each do |folder| %>
<tr>
<td><%= link_to(h(folder.filename),
{:action => 'download',
:id => folder.id}) %>
</td>
<td><%= h(folder.content_type) %></td>
<td align="right"><%= folder.size.to_s.reverse.gsub( /(\d{3})(?=\d)/, '\1,' ).reverse %></td>
<td>
<%= link_to('[destroy]',
{:action => 'destroy',
:id => folder.id}) %>
</td>
</tr><% end %>
</table><%= link_to('new', {:action => 'new'}) %>
(3) 漢字を含むファイル名をアップロードするときの不具合を修正
文字コードがWindowsとRuby on Railsでは異なるため、漢字を含むファイル名のときはファイル名称が正しく伝わりません。
例えばUTF8コードで、”すいれん_Water lilies.jpg”のファイルをそのままWindowsに渡すと”縺吶>繧後s_Water lilies.jpg”となってしまいます。
そこでRuby on Rails からWindowsOSへファイルを渡すときに文字コードをutf8からshiftjisにコンバージョンするロジックを組み込みます。
class Attachment < ActiveRecord::Base
MAX_FILE_SIZE = 1.megabytevalidates_presence_of :filename, :message => 'Please choose a file.'
validates_inclusion_of :size, :in => (1..MAX_FILE_SIZE),
:message => "File size is too big or 0. Maximum size is #{MAX_FILE_SIZE} bytes."def file=(file)
self.filename = file.original_filename if file.respond_to?(:original_filename)
self.content_type = file.content_type if file.respond_to?(:content_type)
self.size = file.size if file.respond_to?(:size)
@tmp = file
enddef after_save
File.open(filepath, "wb") { |f|
f.write @tmp.read
}
end
def after_destroy
File.delete filepath if File.exist? filepath
end
def filepath
str=self.filename
str_shiftjis = str.kconv(Kconv::SJIS, Kconv::UTF8) #UTF8 から Shift_JIS に変換
"public/files/#{self.id}_#{str_shiftjis}"
end
end
(4) 漢字を含むファイル名をダウンロードするときの不具合を修正
同様にダウンロードのときもRuby on RailsからWindowsにファイルを渡すとき、漢字を含むファイル名は正しく伝わりません。
そこで文字コードをutf8からshiftjisにコンバージョンするロジックを組み込みます。
これでファイル名はWindowsへも正しく引き継がれます。
class FoldersController < ApplicationController
:
:
def download
@folder = Folder.find(params[:id])
str=@folder.filename
str_shiftjis = str.kconv(Kconv::SJIS, Kconv::UTF8) # UTF8 から Shift_JIS に変換
send_file(@folder.filepath,
{:filename => str_shiftjis,
:type => @folder.content_type})
end
end
(5) 文字コード関係のまとめ
アップロードおよびダウンロードにおける文字コードの推移については次の図が理解しやすいでしょう。