Rails:允许下载存储在S3上的文件,而不向用户显示实际的S3 URL

我在Heroku上托管了一个Rails应用程序。 该应用程序在Amazon S3上生成并存储PDF文件。 用户可以下载这些文件以便在浏览器中查看或保存在他们的计算机上。

我遇到的问题是虽然可以通过S3 URL下载这些文件(如“https://s3.amazonaws.com/my-bucket/F4D8CESSDF.pdf”),但这显然不是一个好方法。它。 不希望向用户暴露关于后端的大量信息,更不用说上升的安全问题了。

是否有可能让我的应用以某种方式从控制器中的S3检索文件数据,然后为用户创建下载流,以便不暴露亚马逊URL?

是的,这是可能的 – 只需使用Rails获取远程文件并将其临时存储在服务器上或直接从缓冲区发送。 这个问题当然是您需要先获取文件才能将其提供给用户。 请参阅此主题进行讨论,他们的解决方案如下:

#environment.rb require 'open-uri' #controller def index data = open(params[:file]) send_data data, :filename => params[:name], ... end 

这个问题也有些相关 。

您可以将s3对象创建为私有,并使用url_for方法为它们生成临时公共URL(aws-s3 gem)。 这样,您就不会通过应用程序服务器流式传输文件,这样可以提高可扩展性。 它还允许基于会话的授权(例如,在您的应用程序中设计),跟踪下载事件等。

为此,请将指向s3托管文件的直接链接更改为指向控制器/操作的链接,以创建临时URL并重定向到该控制器/操作。 像这样:

 class HostedFilesController < ApplicationController def show s3_name = params[:id] # sanitize name here, restrict access to only some paths, etc AWS::S3::Base.establish_connection!( ... ) url = AWS::S3::S3Object.url_for(s3_name, YOUR_BUCKET, :expires_in => 2.minutes) redirect_to url end end 

在下载url中隐藏亚马逊域通常使用DNS别名来完成。 您需要创建子域的CNAME记录别名 ,例如downloads.mydomain ,到s3.amazonaws.com 。 然后,您可以在AWS::S3::Base.establish_connection!(:server => "downloads.mydomain", ...)指定:server选项AWS::S3::Base.establish_connection!(:server => "downloads.mydomain", ...) ,S3 gem将使用它来生成链接。

首先,您需要在您的域中创建一个CNAME,就像在这里解释一样。

其次,您需要创建一个与CNAME中的名称相同的存储桶。

要完成,您需要在config / initializers / carrierwave.rb中添加此配置:

 CarrierWave.configure do |config| ... config.asset_host = 'http://bucket_name.your_domain.com' config.fog_directory = 'bucket_name.your_domain.com' ... end