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?
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)
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"
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:
send_data
ne produit pas le "téléchargement par navigateur" attendu.download
.Mon implémentation ressemble à ceci:
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
<%= 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.
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.
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