【问题标题】:Using Send_File to a Remote Source (Ruby on Rails)使用 Send_File 到远程源(Ruby on Rails)
【发布时间】:2009-08-24 12:20:01
【问题描述】:

在我的应用程序中,我有一个让我很头疼的要求。

我有一个文件存储在 S3 中,当用户点击我的应用程序中的链接时,我登录他们点击链接的数据库,将他们的“下载信用”限额减少一,然后我想提示下载文件。

我不只是想将用户重定向到该文件,因为它存储在 S3 中,并且我不希望他们拥有源文件的链接(以便我可以保持完整性和访问权限)

看起来 send_file() 无法与远程源文件一起使用,有人推荐一个 gem 或合适的代码来做到这一点吗?

【问题讨论】:

标签: ruby-on-rails


【解决方案1】:

您需要在从 S3 存储桶/对象读取文件内容时将文件内容流式传输给用户。

如果你使用AWS::S3 库,这样的东西可能会起作用:

 send_file_headers!( :length=>S3Object.about(<s3 object>, <s3 bucket>)["content-length"], :filename=><the filename> )
 render :status => 200, :text => Proc.new { |response, output|
   S3Object.stream(<s3 object>, <s3 bucket>) do |chunk|
     output.write chunk
   end
 }

此代码主要是从 send_file 代码中复制而来,该代码本身仅适用于本地文件或类似文件的对象

注意无论如何,我建议不要从 rails 进程本身提供文件。如果可能/可以接受您的用例,我会使用 经过身份验证的 GET 来提供存储桶中的私有数据。

使用经过身份验证的 GET,您可以使存储桶及其对象保持私有,同时允许通过制作包含身份验证签名令牌的 URL 来读取特定对象内容的临时权限。用户被简单地重定向到经过身份验证的 URL,并且令牌可以在几分钟内有效。

使用上面提到的 AWS::S3 你可以通过这种方式获得一个经过身份验证的 GET url:

 time_of_exipry = Time.now + 2.minutes
 S3Object.url_for(<s3 object>, <s3 bucket>,
                  :expires => time_of_exipry)

【讨论】:

  • S3Ojbect.url_for 看起来是最好的方法
  • 我们使用重定向一年,但现在我们真的希望 Safari/Mac 工作:stackoverflow.com/questions/1995589/…send_file_headers!是私有的, send_data 或 send_file 都不接受远程文件;解决方法,但仅供参考
  • 我觉得你生成time_of_expiry的方式其实行不通,你需要这样做:time_of_expiry = 2.minutes.from_now.to_i
  • 或者你也可以:expires_in =&gt; 60 * 2
  • 或者您可以避免使用:expires_in 键并让它在 5 分钟内过期,这是默认设置。 S3 documentation
【解决方案2】:

使用临时文件的完整图像下载方法(经过测试的rails 3.2):

def download
  @image = Image.find(params[:image_id])

  open(@image.url) {|img|
    tmpfile = Tempfile.new("download.jpg")
    File.open(tmpfile.path, 'wb') do |f| 
      f.write img.read
    end 
    send_file tmpfile.path, :filename => "great-image.jpg"
  }   
end

【讨论】:

  • 像魅力一样工作!临时文件在 GC 上被自动删除!
【解决方案3】:

您可以从 S3 读取文件并将其写入本地非公共目录,然后使用 X-Sendfile (apache) 或 X-Accel-Redirect (nginx) 来提供内容。

对于 nginx,您可以在配置中包含以下内容:


            location /private {
                                 internal;
                                 alias /path/to/private/directory/;
            }

然后在您的 Rails 控制器中,您执行以下操作:


   response.headers['Content-Type'] = your_content_type
   response.headers['Content-Disposition'] = "attachment; filename=#{your_file_name}"
   response.headers['Cache-Control'] =  "private"
   response.headers['X-Accel-Redirect'] = path_to_your_file
   render :nothing=>true

一个很好的过程记录是here

【讨论】:

  • 您说的是正确的,但是如果文件不位于 Web 服务器的本地,是否有任何方法可以做到这一点?手头的问题是文件存储在 S3 Web 服务上。
  • 我不认为您可以授予每个用户对您的 S3 文件的直接访问权限,除非他们拥有自己的 S3 帐户。您必须将文件从 S3 下载到本地服务器并从那里发送。
  • s3 文件通常是公开的,只是使用了经过混淆的 URI。似乎您尝试实现的效果无需复制文件即可完成,请参阅此处的讨论:ruby-forum.com/topic/132214。但是,您必须执行 2 个阶段:登录,然后发送文件请求。
  • 我刚刚在 s3 api 中验证了 Luca 的方法,未经测试但看起来正确 - +1
猜你喜欢
  • 2014-01-05
  • 2023-04-08
  • 2014-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-29
  • 1970-01-01
相关资源
最近更新 更多