使用Send_File到远程源(Ruby on Rails)

时间:2023-02-06 08:06:24

In my app, I have a requirement that is stumping me.

在我的应用程序中,我有一个要求让我感到难过。

I have a file stored in S3, and when a user clicks on a link in my app, I log in the DB they've clicked the link, decrease their 'download credit' allowance by one and then I want to prompt the file for download.

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

I don't simply want to redirect the user to the file because it's stored in S3 and I don't want them to have the link of the source file (so that I can maintain integrity and access)

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

It looks like send_file() wont work with a remote source file, anyone recommend a gem or suitable code which will do this?

看起来send_file()不能使用远程源文件,任何人都推荐宝石或合适的代码来执行此操作?

3 个解决方案

#1


9  

You would need to stream the file content to the user while reading it from the S3 bucket/object.

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

If you use the AWS::S3 library something like this may work:

如果您使用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
 }

This code is mostly copied form the send_file code which by itself works only for local files or file-like objects

此代码主要通过send_file代码复制,该代码本身仅适用于本地文件或类文件对象

N.B. I would anyhow advise against serving the file from the rails process itself. If possible/acceptable for your use case I'd use an authenticated GET to serve the private data from the bucket.

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

Using an authenticated GET you can keep the bucket and its objects private, while allowing temporary permission to read a specific object content by crafting a URL that includes an authentication signature token. The user is simply redirected to the authenticated URL, and the token can be made valid for just a few minutes.

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

Using the above mentioned AWS::S3 you can obtain an authenticated GET url in this way:

使用上面提到的AWS :: S3,您可以通过以下方式获取经过身份验证的GET URL:

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

#2


3  

You can read the file from S3 and write it locally to a non-public directory, then use X-Sendfile (apache) or X-Accel-Redirect (nginx) to serve the content.

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

For nginx you would include something like the following in your config:

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


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

Then in your rails controller, you do the following:

然后在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

A good writeup of the process is here

这里有一个很好的写作过程

#3


3  

Full image download method using temp file (tested rails 3.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

#1


9  

You would need to stream the file content to the user while reading it from the S3 bucket/object.

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

If you use the AWS::S3 library something like this may work:

如果您使用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
 }

This code is mostly copied form the send_file code which by itself works only for local files or file-like objects

此代码主要通过send_file代码复制,该代码本身仅适用于本地文件或类文件对象

N.B. I would anyhow advise against serving the file from the rails process itself. If possible/acceptable for your use case I'd use an authenticated GET to serve the private data from the bucket.

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

Using an authenticated GET you can keep the bucket and its objects private, while allowing temporary permission to read a specific object content by crafting a URL that includes an authentication signature token. The user is simply redirected to the authenticated URL, and the token can be made valid for just a few minutes.

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

Using the above mentioned AWS::S3 you can obtain an authenticated GET url in this way:

使用上面提到的AWS :: S3,您可以通过以下方式获取经过身份验证的GET URL:

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

#2


3  

You can read the file from S3 and write it locally to a non-public directory, then use X-Sendfile (apache) or X-Accel-Redirect (nginx) to serve the content.

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

For nginx you would include something like the following in your config:

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


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

Then in your rails controller, you do the following:

然后在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

A good writeup of the process is here

这里有一个很好的写作过程

#3


3  

Full image download method using temp file (tested rails 3.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