Ruby on rails 使用send_file从AmazonS3下载文件?
我的应用程序中有一个下载链接,用户可以从中下载存储在s3上的文件。这些文件将在URL上公开访问,URL类似Ruby on rails 使用send_file从AmazonS3下载文件?,ruby-on-rails,ruby,ruby-on-rails-3,amazon-s3,ruby-on-rails-3.2,Ruby On Rails,Ruby,Ruby On Rails 3,Amazon S3,Ruby On Rails 3.2,我的应用程序中有一个下载链接,用户可以从中下载存储在s3上的文件。这些文件将在URL上公开访问,URL类似 https://s3.amazonaws.com/:bucket_name/:path/:to/:file.png 下载链接点击我的控制器中的操作: class AttachmentsController < ApplicationController def show @attachment = Attachment.find(params[:id]) sen
https://s3.amazonaws.com/:bucket_name/:path/:to/:file.png
下载链接点击我的控制器中的操作:
class AttachmentsController < ApplicationController
def show
@attachment = Attachment.find(params[:id])
send_file(@attachment.file.url, disposition: 'attachment')
end
end
该文件确实存在,并且可以在错误消息中的url处公开访问
如何允许用户下载S3文件?为了从web服务器发送文件
- 您需要从S3(请参阅)或
- 您可以
重定向到@attachment.file.expiring\u url(10)
- 您也可以使用
我喜欢这个选项,因为你有更好的控制。您没有将用户发送到s3,这可能会让一些用户感到困惑
我只想向
AttachmentsController添加一个下载方法
def download
data = open("https://s3.amazonaws.com/PATTH TO YOUR FILE")
send_data data.read, filename: "NAME YOU WANT.pdf", type: "application/pdf", disposition: 'inline', stream: 'true', buffer_size: '4096'
end
并添加路线
get "attachments/download"
以下是最终对我有效的方法。从S3对象获取原始数据,然后使用
send_data
将其传递到浏览器
使用此处找到的aws sdkgem文档
完全控制器方法
def download
AWS.config({
access_key_id: "SECRET_KEY",
secret_access_key: "SECRET_ACCESS_KEY"
})
send_data(
AWS::S3.new.buckets["S3_BUCKET"].objects["FILENAME"].read, {
filename: "NAME_YOUR_FILE.pdf",
type: "application/pdf",
disposition: 'attachment',
stream: 'true',
buffer_size: '4096'
}
)
end
为用户保持简单
我认为最好的处理方法是使用一个即将过期的S3URL。其他方法存在以下问题:
- 文件首先下载到服务器,然后再下载到用户
- 使用
不会产生预期的“浏览器下载”send_data
- 绑定Ruby进程
- 需要额外的
下载
控制器操作
附件.rb
在你看来
感谢他的指导。我刚刚将我的
public/system
文件夹迁移到Amazon S3。上面的解决方案有帮助,但我的应用程序接受不同类型的文档。因此,如果您需要相同的行为,这对我很有帮助:
@document = DriveDocument.where(id: params[:id])
if @document.present?
@document.track_downloads(current_user) if current_user
data = open(@document.attachment.expiring_url)
send_data data.read, filename: @document.attachment_file_name, type: @document.attachment_content_type, disposition: 'attachment'
end
文件保存在驱动文档
对象的附件
字段中。
我希望这能有所帮助。def下载\u pdf
@post=@post.avatar.service\u url
send_data(
"#{Rails.root}/public/#{@post}",
filename: "#{@post}",
type: "image/*",
disposition: 'inline', stream: 'true', buffer_size: '4096'
)
结束
如何允许用户下载S3文件
如果您能够在将文件上载到S3之前在文件上设置一些元数据,而不是在用户稍后需要下载时尝试对其进行修补,则此解决方案简单得多:
如果使用的是fog
,则可以执行以下操作:
has_attached_file :report,
fog_file: lambda { |attachment|
{
content_type: 'text/csv',
content_disposition: "attachment; filename=#{attachment.original_filename}",
}
}
如果您使用AmazonS3作为存储提供商,那么
这样应该可以:
has_attached_file :report
s3_headers: lambda { |attachment|
{
'Content-Type' => 'text/csv',
'Content-Disposition' => "attachment; filename=#{attachment.original_filename}",
}
}
如何将其用于S3上的非公共文件?@MehulKar在这种情况下,您需要使用
@attachment.file.expiring_url
注意:如图所示,访问S3上的私有文件时,S3 url将包含您的秘密AWS凭据,以便浏览器可以发出经过身份验证的请求。如果下载有效并且用户流保持在您的页面上,这并不明显,但是当下载无效时(例如文件不存在),S3上的错误页面将在URL上显示凭据。这是一个相当大的安全风险。重定向到url不会自动下载文件,它只会将您带到它的浏览器视图,然后您可以在那里下载它。因此在此解决方案中,open
方法不会先下载整个文件?而send_data可以将文件从amazon流式传输给用户,而用户根本不知道真正的s3文件路径?肯定是这样,但是对于大文件,似乎不需要流
&缓冲区大小
选项,对于等待服务器先下载文件然后将其流式传输给用户的用户来说,这可能会造成混淆。另外,它需要的时间是用户直接从S3下载的时间的两倍。使用carrierwave+S3文件,我使其工作如下:article=article.find params[:id]
file\u data=open(article.file.url)
发送数据文件\u data.read,文件名:article.filename,type:article.file.content\u type,处置:'attachment'值得指出的是,除了@JoshPinter观察到它的速度较慢(因为数据通过中间层而不是直接传输),它还会给服务器带来额外的负载并阻塞,尽管您可以将其卸载到后台任务中。直接从S3下载文件效率更高,加上S3有相当多的错误页,但这必须在发送给用户之前将文件下载到服务器,对吗?对,根据文件的大小,可能不是一个选项。在我的例子中,它们是小PDF,这是可以接受的。这会下载整个存储桶吗?我有一个类似的方法,根据各个bucket对象的键下载它们。@BigRon不,只下载bucket中的单个对象。但再看看我的代码片段,我想我已经切掉了一个重要的部分!谢谢你指出这一点@比格伦,现在看看。添加了使用S3桶实际获取对象的部分。很好的更正,看起来不错。我必须尝试您使用self.path
。它看起来比我现在使用的更简单method@BigRon请告诉我self.path
是否适用于您,因为我们实际上有一个删除前导斜杠的检查,但我不确定这是否在所有情况下都是必要的,或者我们是否有一些特殊的情况:self.path.chr=='/'?self.path[1..-1]:self.path)
如果可能,请尽力提供其他解释,而不仅仅是代码。这些答案往往更有用,因为它们有助于社区成员,特别是新开发人员更好地理解解决方案的理由,并有助于避免需要解决后续问题。如果可能,请努力提供额外的解释,而不仅仅是代码。这些答案往往更有用,因为它们有助于社区成员,特别是新开发人员更好地理解其中的原因
@document = DriveDocument.where(id: params[:id])
if @document.present?
@document.track_downloads(current_user) if current_user
data = open(@document.attachment.expiring_url)
send_data data.read, filename: @document.attachment_file_name, type: @document.attachment_content_type, disposition: 'attachment'
end
send_data(
"#{Rails.root}/public/#{@post}",
filename: "#{@post}",
type: "image/*",
disposition: 'inline', stream: 'true', buffer_size: '4096'
)
has_attached_file :report,
fog_file: lambda { |attachment|
{
content_type: 'text/csv',
content_disposition: "attachment; filename=#{attachment.original_filename}",
}
}
has_attached_file :report
s3_headers: lambda { |attachment|
{
'Content-Type' => 'text/csv',
'Content-Disposition' => "attachment; filename=#{attachment.original_filename}",
}
}