依據前一日所提及的目標用 ROR 改寫第十一日的例子,透過資料庫的結合,做出基本的檔案列表、上傳、下載的動作。
修改儲存檔案的動作
建立一個 model 來將上傳的檔案相關資訊存在該 table 之中:
./script/generate model upfile filename:string filetoken:string filetype:string size:string
rake db:migrate
其中的 filetoken ,是想把任何上傳的檔案另外命名為獨一無二的檔名,不致與其他名稱重覆,所以利用下面這一個 function 來做出供 URL 的獨一檔名代表,以及做為資料庫讀取哪一筆檔案的依據,其實也可以當作permalink的一種。
# vi app/controllers/upfile_controller.rb 加到 private 下面
def gettoken
filetoken=Digest::SHA1.hexdigest(Time.now.to_s.split(//).sort_by {rand}.join)
return filetoken
end
利用 http://blog.vixiom.com/2007/12/28/hacking-attachment_fu-to-work-with-flashflex-uploads-and-crop-square-images/所提供的方式,把檔案的屬性讀出來:
# vi app/controllers/upfile_controller.rb 加到 private 下面
def get_content_type(filewhere)
content_type = `file -bi "#{filewhere}"`.chomp
content_type = fallback unless $?.success?
content_type.gsub!(/;.+$/,"") if content_type
return content_type
end
所上傳的檔案放在 /home/ironman/test1/FILES 裡,若檔案放在 public 目錄中則無法達到管控的目的。
修改第十一日的 save_file 的區塊為:
def save_file
#處理上傳檔案欄是空的情況下鍵入上傳的情形
if params['filename'].nil? or params['filename'] == ''
flash[:error] = "沒有檔名!"
redirect_to :action => 'index'
return
end
# 取一個獨一的代表檔名
@filetoken = gettoken
# 原始檔名
filename = params['filename'].original_filename
# 指定將存到的位置及檔名
filewhere = "/home/ironman/test1/FILES/#{@filetoken}"
# 真正的儲存動作
File.open("#{filewhere}", "wb") do |f|
f.write(params['filename'].read)
end
# 讀出該檔的格式、大小為何
@content_type = get_content_type(filewhere)
@size = File.size(filewhere)
# 上述所得的檔案資訊存到 table 裡
Upfile.new
Upfile.create(:filename => filename,
:filetoken => @filetoken,
:filetype => @content_type,
:size => @size)
flash[:notice] = "檔案上傳成功! #{@content_type} :: #{@size}"
redirect_to :action => 'index'
end
這時上傳成功就會出現檔案屬性及大小的數字來。
這時可看 FILES 的目錄有16進位字串的檔名存在裡面。
建立下載的動作
def dl
# 從資料庫抓出所 query 的項目,因為不想用檔案的 id 來作參數,而改用 filetoken 做為存取依據
@dlfile = Upfile.find_by_filetoken(params[:filetoken])
# 如果query的網址,在資料庫中沒此筆資料則中止下載動作,並導回到首頁
if @dlfile.nil?
flash[:error] = "沒有這個檔案"
redirect_to :action => 'index'
return
end
# 若無上述中止動作則開始將檔名
send_file("/home/ironman/test1/FILES/#{@dlfile.filetoken}",
:filename => "#{@dlfile.filename}",
:type => "#{@dlfile.filetype}",
:stream => true,
# 以下的 disposition 兩種選一種用,看是要用儲存檔案的方式,或直接秀在瀏覽器中。
:disposition => "attachment")
# :disposition => "inline")
end
建立檔案列表及下載連結
將所有檔案列出,並以最新上傳的檔案,最先排出來:
# vi app/controllers/upfile_controller.rb
def index
# @dir = get_local_list
@files = Upfile.find(:all, :order => 'created_at desc')
end
修改 app/views/upfile/index.html.erb 檔
<h1>Upfile#index</h1>
<%= form_tag({:action=>'save_file'}, :multipart => true)%>
<input type="file" name="filename" />
<input type="submit" name="Upload" value="Upload"/>
</form>
<hr noshade width = "90%" color = "red" />
<table border=1>
<tr><td>檔名</td><td>格式</td><td>大小</td><td>日期</td></tr>
<% @files.each do |file| -%>
<tr>
<td><%= link_to file.filename, :action => 'dl', :filetoken => file.filetoken -%></td>
<td><%= file.filetype -%></td>
<td><%= file.size -%></td>
<td><%= file.created_at -%></td>
<% end -%>
</tr>
</table>
這樣就可以列出所有可下載的檔案及相關資訊。
截至目前的建置,即可有最簡單的上傳,並有清楚的檔案大小、上傳日期及檔案格式;已經是與第十二日舊的作法有同樣的功能,是因為上傳的各檔都有個別 hash 的16進位的字串作為代表,所以不會有相同檔名覆蓋問題,而且 query 的參數也不致有編碼上的問題。接下來將再加上刪除功能以及改變一些呈獻的畫面。