8

我正在尝试创建一个简单的链接,单击该链接会启动与特定资源关联的文档的下载。我有一个资源模型,在该模型中有一个名为“文档”的列。单击链接时,我能够成功地查看内联文档,但我更愿意下载它。我一直在阅读有关内容类型和 send_file 的内容,但我无法将它们完全拼凑起来以发挥作用。

这是我认为我需要用于链接的代码:
<%= link_to 'Download File', :action => "download_file" %>

这会引发错误:
ResourcesController#show 中的 ActiveRecord::RecordNotFound 找不到 id=download_file 的资源

当我将链接更改为此时,它会在浏览器中打开文件:
<%= link_to 'Open file', resource.document_url, :target => "_blank" %>

在我的 ResourcesController 我定义了这个方法:

  def download_file
  @resource = Resource.find(params[:id])
  send_file(@resource.file.path,
        :filename => @resource.file.name,
        :type => @resource.file.content_type,
        :disposition => 'attachment',
        :url_based_filename => true)
  end

我在 routes.rb 中设置了一条路线,如下所示:

  resources :resources do
    get 'resources', :on => :collection
  end

因此,基于此错误,我在 ResourcesController 中的 download_file 方法似乎无法确定与文档关联的资源记录的 id。

我正在运行:Rails 3.2.11 Carrierwave 0.8.0 Ruby 1.9.3-p194

我希望能对此有所了解。我搜索了许多文章,但找不到简单的教程。谢谢。

4

1 回答 1

13

我能够弄清楚这一点。我将尝试解释我是如何解决这个问题的。

download_file 方法正确地位于 ResourcesController 中,但我没有使用正确的变量名。对于我的目的,这是正确的方法定义:

  def download_file
  @resource = Resource.find(params[:id])
  send_file(@resource.document.path,
        :type => 'application/pdf',
        :disposition => 'attachment',
        :url_based_filename => true)
  end

在我的情况下,我在变量@resource 中返回了一条记录,该记录有一个与之关联的文档(同样,我使用的是 Carrierwave)。所以 send_file 需要的第一个参数是路径(参见send_file 的 API)。此路径信息由@resource.document.path 提供。

send_file 接受许多其他参数,这些参数似乎是可选的,因为我只需要其中三个。有关其他参数,请参阅 API 文档。

为我抛出错误的一个是类型:。我将变量“@resource.file.content_type”传递给它,但它显然是在寻找文字。一旦我通过了它的应用程序/pdf,它就起作用了。如果没有明确设置,下载的文件没有添加文件扩展名。无论如何,对于我的应用程序,此文档可能是各种不同的 mime 类型,从 pdf 到 word 再到 mp4。所以我不确定如何指定多个。

对于我的目的(下载而不是在浏览器中显示)来说,真正重要的论点是 :disposition 需要设置为“附件”而不是“内联”。

另一个参数 :url_based_filename 显然用于从 URL 确定文件的名称。由于我没有提供文件名,也许这是我向正在下载的文件提供文件名的唯一方法,但我不确定这一点。

我还需要将 link_to 标记更改为:
<%= link_to 'Download File', :action => 'download_file', :id => resource.id %>

请注意,为 :id 符号提供当前资源的 id 提供了识别相关资源所需的特定信息。

我希望这对其他人有所帮助。这对其他人来说一定很明显,因为我没有找到任何文件下载文件的基础教程。我还在学习很多东西。

更新:我不得不玩弄这个以获得所需的行为。这是有效的新方法:

      def download_file
        @resource = Resource.find(params[:id])
        send_file(@resource.document.path,
              :disposition => 'attachment',
              :url_based_filename => false)
      end

通过这些更改,我不再需要设置 :type。设置 ":url_based_filename => true" 会继续在 URL 中的记录 id 之后命名文件。根据我对该参数功能的阅读,将其设置为 false 似乎违反直觉,但是,这样做会产生以实际文件名命名文件的预期结果。

我还使用了mime-types gem并将以下内容添加到我的 video_uploader.rb 文件中:

require 'carrierwave/processing/mime_types'

class VideoUploader < CarrierWave::Uploader::Base
include CarrierWave::MimeTypes
process :set_content_type

这个 gem 显然设置了从文件名中的文件扩展名猜测的 mime 类型。这样做使我不必在我的 download_file 方法内的 send_file 参数中显式设置 :type 。

于 2013-03-10T21:35:17.597 回答