是否可以通过 CarrierWave 从安全资源下载文件,并通过 Rails 控制器将其提供给浏览器?

Is it possible to download a file via CarrierWave, from a secure resource, and serve it to the browser through the Rails controller?

我有一个现有的 Rails 4 应用程序,我在其中使用 Carrierwave 来处理附加的 Office 文档。 Carrierwave 由 Azure blob 存储提供支持。几个月来效果很好。

最近,IT 人员以不同的名称在应用程序中创建了一个帐户,并发现您可以通过普通 Web 浏览器从 (public) blob 容器下载文件,如只要您知道大约 200 个字符的确切路径和文件名组合。对我来说,这并不比文件通过 sneakernet 离开网络更糟糕,但是,当然,这种情况已被视为重要公司信息的无法承受的安全风险。

我一直在想办法让我的附件控制器为我下载文件,并将其提供给浏览器,这样我就可以关闭 public 对 blob 的访问容器。 Carrierwave 应该了解如何安全地下载该文件,因为它已经配置了位置、主机和安全密钥,但是,对于我来说,我无法弄清楚如何调用库中的某些方法这样做。

我得到的最接近的是 this module,这看起来很有希望,但我不知道如何以有用的方式调用它。它还想通过public类型的URL.

来处理文件

我也试过创建一个临时文件,编写它,然后 send_file-ing 它,但它似乎只提供一个 html 文件,并带有一条关于无法访问临时文件的错误消息文件。

  def show
    suffix = /(?<=\.)[^.]*$/.match(@attachment.document.path)[0]
    f = Tempfile.new([@attachment.filename, suffix], encoding: 'ascii-8bit')
    f.write @attachment.document.read
    send_file f, :disposition => 'attachment'
    f.close
    f.unlink
  end

@attachment.document.read 给我文件的 ASCII 转储,但我找不到将其读取为二进制文件的方法。

我一直在想 CarrierWave 中一定有一些简单的方法可以告诉它 "go get this file via the same way you stored it",但我找不到它。

上面我的代码有 2 个问题。首先,在调试中,我注意到使用 f 作为临时文件变量名存在名称冲突。 (与内核有关。)

其次,呈现动作的方式,调用 send_file 方法时并未发送文件。在此之前,它实际上还经历了一些 Rails 圈。所以我无法在该行之后立即清理文件,或者当 Rails 最终完成渲染时,文件不存在无法发送给客户端。 (无论如何我都不应该清理文件,因为 Tempfile 旨在将文件放在系统清理的地方。)

所以这有效:

  def show
    suffix = /\.[^.]*$/.match(@attachment.document.path)[0]
    scratch_file = Tempfile.new([@attachment.filename, suffix], encoding: 'ascii-8bit')
    scratch_file.write @attachment.document.read
    send_file scratch_file, :disposition => 'attachment'
  end