在Rails中,无法覆盖Content-Disposition文件名

9

我正在使用Rails 3.1.0.rc8和Chromium 15.0.874.102。

我想设置CSV下载的文件名。我正在按照这个SO解决方案进行操作,但是发现自己无法修改Content-Disposition头的filename

这是我的代码:

module ActionController 
  module CSVHelper

    def render_csv options={}
      if request.env['HTTP_USER_AGENT'] =~ /msie/i
        headers['Pragma'] = "public"
        headers['Content-Type'] = "text/plain"
        headers['Cache-Control'] = "no-cache, must-revalidate, post-check=0, pre-check=0"
        headers['Expires'] = "0"
      else
        headers['Content-Type'] = "text/csv"
      end

      filename = generate_filename options.delete(:basename)
      headers['Content-Disposition'] = "attachment; filename=#{filename}"
    end

    def generate_filename basename=nil, suffix="csv"
      filename = basename || params[:action]
      filename << ".#{suffix}"
      filename
    end

  end
end    

在我的控制器中:

respond_to do |format|
  format.html
  format.csv do
    render_csv(:basename => "my_filename")
    Rails.logger.debug "HEADERS: #{headers.to_s}"
  end
end  

在我的日志中:
[2011-11-28 12:25:49.611] DEBUG - HEADERS: {"Content-Type"=>"text/csv", "Content-Disposition"=>"attachment; filename=my_filename.csv"}

在Chromium网络检查工具中,我在响应头中看到以下内容:
Content-Type: text/plain
Content-Disposition: attachment; filename=index.csv

如果我将Content-Type更改为foo/bar之类的内容,我会在网络检查工具中看到更改的结果。无论我将filename设置为什么,它始终是index.csv
谢谢, Max

您的视图设置正确吗? - baol
视图按预期呈现,如果这是您的意思;下载文件的内容看起来像我想要的。 - maxenglander
4个回答

12

根据HTTP/1.1规范附录中的Content-Disposition章节,文件名必须是一个引用字符串

   filename-parm = "filename" "=" quoted-string
   ...

一个例子是

   Content-Disposition: attachment; filename="fname.ext"
所以您可能需要进行以下更改(请注意引号):
headers['Content-Disposition'] = "attachment; filename=\"#{filename}\""

请注意,如果文件名本身包含引号,则需要对其进行转义 - filename.gsub(/"/, '\\"') - gmcnaughton

2
我发现我的团队正在使用csv_builder gem,它允许您通过在控制器中设置@filename来调整下载文件的名称。仍将奖励正确答案的人,如果有人能解释为什么我会得到OP中描述的行为。

2

对于这种类型的内容,我个人不使用render,而是更喜欢使用send_data。以下是一个例子:

send_data data, :type => 'text/csv', :disposition => 'attachment; filename=my_file_name.csv'

1

我没有看到你实际发送/渲染输出的地方...是在.csv.erb文件中还是其他什么地方使用了csv_builder?这可能是诀窍。

我用.xlsx输出得到了同样的结果。一旦我把文件保存在磁盘上,我就可以这样做:

def example
  respond_to do |format|
    format.xlsx do
      path = some_method_generating_xlsx_file
      headers['Content-Disposition'] = "attachment; filename=test.xlsx"
      render :text => File.binread(path), :content_type => XLSX_MIME_TYPE
    end
  end
end

在我的控制器中,它运行得很好。也许是模板渲染导致了头部被覆盖的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接