web-dev-qa-db-fra.com

Vous utilisez send_file pour télécharger un fichier depuis Amazon S3?

J'ai un lien de téléchargement dans mon application à partir duquel les utilisateurs devraient pouvoir télécharger des fichiers stockés sur s3. Ces fichiers seront accessibles au public sur des URL qui ressemblent à quelque chose comme

https://s3.amazonaws.com/:bucket_name/:path/:to/:file.png

Le lien de téléchargement frappe une action dans mon contrôleur:

class AttachmentsController < ApplicationController
  def show
    @attachment = Attachment.find(params[:id])
    send_file(@attachment.file.url, disposition: 'attachment')
  end
end

Mais j'obtiens l'erreur suivante lorsque j'essaie de télécharger un fichier:

ActionController::MissingFile in AttachmentsController#show

Cannot read file https://s3.amazonaws.com/:bucket_name/:path/:to/:file.png
Rails.root: /Users/user/dev/Rails/print

Application Trace | Framework Trace | Full Trace
app/controllers/attachments_controller.rb:9:in `show'

Le fichier existe définitivement et est accessible au public à l'url dans le message d'erreur.

Comment autoriser les utilisateurs à télécharger des fichiers S3?

55
David Tuite

Pour envoyer un fichier depuis votre serveur Web,

  • vous devez le télécharger depuis S3 (voir @ réponse de nzajt ) ou

  • vous pouvez redirect_to @attachment.file.expiring_url(10)

29
sumskyi

Vous pouvez aussi utiliser send_data .

J'aime cette option car vous avez un meilleur contrôle. Vous n'envoyez pas d'utilisateurs à s3, ce qui peut prêter à confusion pour certains utilisateurs.

Je voudrais simplement ajouter une méthode de téléchargement au 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 

et ajoutez l'itinéraire

get "attachments/download"
79
nzajt

Gardez les choses simples pour l'utilisateur

Je pense que la meilleure façon de gérer cela est d'utiliser une URL S3 expirante. Les autres méthodes ont les problèmes suivants:

  • Le fichier est d'abord téléchargé sur le serveur, puis sur l'utilisateur.
  • En utilisant send_data ne produit pas le "téléchargement par navigateur" attendu.
  • Lie le processus Ruby.
  • Nécessite une action supplémentaire du contrôleur download.

Mon implémentation ressemble à ceci:

Dans votre attachment.rb

def download_url
  S3 = AWS::S3.new.buckets[ 'bucket_name' ] # This can be done elsewhere as well,
                                            # e.g config/environments/development.rb

  url_options = { 
    expires_in:                   60.minutes, 
    use_ssl:                      true, 
    response_content_disposition: "attachment; filename=\"#{attachment_file_name}\""
  }

  S3.objects[ self.path ].url_for( :read, url_options ).to_s
end

À votre avis

<%= link_to 'Download Avicii by Avicii', attachment.download_url %>

C'est ça.


Si vous vouliez toujours conserver votre action download pour une raison quelconque, utilisez simplement ceci:

Dans votre attachments_controller.rb

def download
  redirect_to @attachment.download_url
end

Merci à guilleva pour ses conseils.

29
Joshua Pinter

Je viens de migrer mon public/system dossier vers Amazon S3. Les solutions ci-dessus aident mais mon application accepte différents types de documents. Donc, si vous avez besoin du même comportement, cela m'aide:

@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

Le fichier est enregistré dans le champ attachment de l'objet DriveDocument. J'espère que ça aide.

5
Kenneth John

Ce qui suit a fini par bien fonctionner pour moi. Récupération des données brutes de l'objet S3, puis utilisation de send_data pour le transmettre au navigateur.

En utilisant le aws-sdk documentation gem trouvée ici http://docs.aws.Amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html

méthode de contrôleur complet

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
4
David Morrow