如何创建临时文件并写入,然后允许用户下载?

时间:2011-12-28 23:00:53

标签: ruby-on-rails

我正在处理我的第一个应用程序,我需要一些帮助,允许我的用户下载包含页面上显示的某些变量的文本文件。

以购物清单为例。 假设您允许用户创建产品购物清单,然后在购物清单页面上显示带有商品的购物清单,
例如。本地主机:3000 /列表/我的列表


看看下面的示例代码(可能不正确):

File.open('shopping_list.txt', 'w') do |file|
  file.puts 'Item 1: #{product_1.name}'
  file.puts 'Item 2: #{product_2.name}'
  file.puts 'Item 3: #{product_3.name}'
end



然后创建具有以下内容的文本文件:

Item 1: Eggs
Item 2: Butter
Item 3: Bread


然后,用户应该可以通过下载链接下载此文件(我不希望该文件存储在服务器上)。

我不知道如何实现这一点,但我希望你们能指导我。 :d


TL; DR

  • 创建用模型数据填充的文本文件(也许创建一个实现此目的的方法?)
  • 文本文件不应存储在服务器上,而是在用户点击下载按钮时创建(不确定这是否为rails方式,但也许有人可以向我展示更好的方式)

5 个答案:

答案 0 :(得分:7)

我假设有一个List资源,其中属性name作为列表名称,列表has_many项目具有属性description

首先,创建下载路径以更改路由config/routes.rb

resources :lists do
  member {get "download"}
end

现在,如果您在控制台中运行rake routes,您应该会看到类似

的路线
/lists/:id/download

你现在应该有更多帮手download_list_url& download_list_path在您的视图中使用

<ul>
<% @lists.each do |list| %>
  <li> <%= list.name %> - <%= link_to 'Download List', download_list_path(list) %> </li>
<% end %>
</ul>

lists_controller中添加操作,并且实际上并不想将文件保留在服务器磁盘上,只是将数据作为字符串流式传输

def download
  list = List.find(params[:id])
  send_data list.as_file,
    :filename => "#{list.name}.txt",
    :type => "text/plain"
end

最后你看到我使用了as_file方法你应该添加到模型中(我不喜欢在控制器,胖模型,瘦控制器中做这些东西)。所以在List模型中

def as_file
  output = [self.name]
  self.items.each {|item| output << item.description }
  output.join("\n")
end

答案 1 :(得分:3)

您说您不想将文件存储在服务器上,而是根据请求“下载”该文件;这听起来像您只想生成并传送文本文档以响应下载链接。有几种方法,但您希望确保设置mime类型,以便浏览器将其视为文本文件而不是html文档。

product_info = [
    "Item 1: #{product_1.name}",
    "Item 2: #{product_2.name}",
    "Item 3: #{product_3.name}",
  ].join("\n")

render :text => product_info # implies :content_type => Mime::Type["text/plain"]

顺便说一下,上面打开/置位的例子不会输出您的想法,因为单引号字符串不会插入。

答案 2 :(得分:3)

所以,你希望:

  • create text files populated with model data (perhaps create a method to achieve this?)
  • text files should not be stored on the server, but created as users click the download button (not sure if this is the rails way but perhaps someone could show me a better way)

你有正确的想法,这是做什么的:

  1. 在模型中创建一个方法以生成文本文件内容。假设此方法称为list_data

  2. 您似乎有一个名为my_list的现有控制器操作。因此我们可以在控制器中调用我们的新方法,如下所示:

  3. def my_list
      # pre-existing code
      respond_to do |format|
        format.html   # show html page as before
        format.text do
          send_data @list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
        end
      end
    end
    

    要链接到下载,只需使用link_to :action => my_list, :format => 'text'

    即可

    有关send_data

    的完整文档,请参阅http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_data 警告&amp;解释:使用上面的方法,实际上没有明确的文件创建,Rails正在为您流式传输。因此,此方法不适用于非常大的文件,或者文件内容的生成需要一段时间。使用延迟方法生成文件并将其存储 - 如果是这种情况,则将文件内容存储在某处 - 但是我们可以在生成后使用send_data

答案 3 :(得分:1)

您可以尝试TempFile和send_file的组合。在你的控制器动作..

file = Tempfile.new('foo')
file.write("hello world")
file.close

send_file file.path

答案 4 :(得分:0)

在Rails 2.3中,您可以使用Template Streaming。使用Redmine我可以记住这样的事情,你必须适应你的情况。参考:Streaming and file downloads

require "prawn" 
class ClientsController < ApplicationController

  # Generate a PDF document with information on the client and return it.
  # The user will get the PDF as a file download.
  def download_pdf
    client = Client.find(params[:id])
    send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
  end

private

  def generate_pdf(client)
    Prawn::Document.new do
      text client.name, :align => :center
      text "Address: #{client.address}" 
      text "Email: #{client.email}" 
    end.render
  end

end

使用Thong Kuah你必须改变“content_type”参数:

def my_list
  # pre-existing code
  respond_to do |format|
    format.html   # show html page as before
    format.text do
      send_data @list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
    end
  end
end