用rails寫一些內部工具的時候, 以前遇到文件下載的相關需求的時候, 都是直接把文件生成到 /public 下面, 但是遇到需要即時生成一堆文件的時候, 這種方式就比較悲劇了,
比如需求要求生成一堆文件, 要一起下載只能生成以后, ZIP 壓縮成一個包下載, 但是如果利用文件系統來存儲, 管理這些數據, 文件生成一堆不說, 光各種 mv, cp, zip操作就夠煩人的了。
rails 提供了很多有用的API來完成文件下載的功能:
1. send_data, send_file 用於文件的下載send_data(data, options = {})
send_file(path, options = {})
這2個的區別在於是否在文件系統中生成文件, 應該send_file就是send_data包了一層, 多了從文件系統讀取文件這個操作。
send_file例子:
IMAGES_PATH = File.join(Rails.root, "public", "images") def download send_file(File.join(IMAGES_PATH, "image.jpg")) end
send_data的一些說明:
send_data的選項有4個,:filename, :type, :disposition, :status
:type是發送文件的資源類型, jpeg格式圖片: "image/jpeg", docx文檔: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
:disposition有2個值:inline或:attachment, 這決定了瀏覽器的打開方式, 是下載還是在瀏覽器中打開send_data例子:
send_data zip_data, filename: "#{file_name}.zip", type: 'application/zip', :disposition => 'attachment'一些額外的說明:
字符串的 force_encoding 會改變源數據的解釋方式, 不過並不會改變源數據, 但是在ruby 1.9 里面發送數據需要force_encoding('BINARY'),
但是我在2.x 里面沒用這個文件也發送成功了, 沒出什么問題。
a = "Ü" a.bytes.to_a #=> [195, 156] a.chars.to_a #=> ["Ü"] a.force_encoding("BINARY") a.bytes.to_a #=> [195, 156] a.chars.to_a #=> ["\xC3", "\x9C"
2. 生成csv文件不經過文件系統, 直接用 send_data 來發送到客戶端
ruby 的CSV庫, 2種方式生成csv文件, 第一種方式生成了 csv 文件后直接保存到文件系統中, 第二種方式直接生成一個csv_string。
這個時候就可以用send_data直接發送數據了。
CSV.open("path/to/file.csv", "wb") do |csv| csv << ["row", "of", "CSV", "data"] csv << ["another", "row"] # ... end
csv_string = CSV.generate do |csv| csv << ["row", "of", "CSV", "data"] csv << ["another", "row"] # ... end
send_data csv_string, :filename => '你看見了一個csv文件.csv', :type => "text/comma-separated-values", :disposition => "attachment"
3. 生成csv文件, 直接生成ZIP文件, 直接用 send_data 發送到客戶端
compressed_filestream = Zip::OutputStream.write_buffer(::StringIO.new('')) do |zos| zos.put_next_entry "第一個文件的名字" zos.print File.read('path') zos.put_next_entry "第二個文件的名字" zos.print file_string end compressed_filestream.rewind return compressed_filestream.read
然后返回的數據直接用send_data發送就好了:
send_data zip_data, filename: "#{file_name}.zip", type: 'application/zip', :disposition => 'attachment'