iT邦幫忙

DAY 13
11

Rails 的簡單任務系列 第 14

[RoR] 以資料庫為依據的上傳下載及列檔功能

依據前一日所提及的目標用 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 的參數也不致有編碼上的問題。接下來將再加上刪除功能以及改變一些呈獻的畫面。


上一篇
[RoR] 適單位內外用暫存空間的規畫
下一篇
[RoR] 刪除功能及顯示方式的變化
系列文
Rails 的簡單任務33

1 則留言

0

謝謝分享咯!

我要留言

立即登入留言