web-dev-qa-db-fra.com

Puis-je diffuser un téléchargement de fichier vers S3 sans en-tête de longueur de contenu?

Je travaille sur une machine avec une mémoire limitée et j'aimerais télécharger un fichier généré dynamiquement (pas à partir du disque) en streaming sur S3. En d'autres termes, je ne connais pas la taille du fichier lorsque je démarre le téléchargement, mais je le saurai à la fin. Normalement, une demande PUT a un en-tête Content-Length, mais il existe peut-être un moyen de contourner cela, comme l'utilisation d'un type de contenu en plusieurs parties ou en morceaux.

S3 peut prendre en charge les téléchargements en continu. Par exemple, voir ici:

http://blog.odonnell.nu/posts/streaming-uploads-s3-python-and-poster/

Ma question est, puis-je accomplir la même chose sans avoir à spécifier la longueur du fichier au début du téléchargement?

53
Tyler

Vous devez télécharger votre fichier en morceaux de 5 Mo + via API multipartie de S . Chacun de ces morceaux nécessite une longueur de contenu, mais vous pouvez éviter de charger d'énormes quantités de données (100 Mo +) en mémoire.

  • Lancer S3 Téléchargement en plusieurs parties .
  • Rassemblez les données dans un tampon jusqu'à ce que ce tampon atteigne la limite inférieure de taille de bloc de S3 (5 Mo). Générez une somme de contrôle MD5 lors de la création du tampon.
  • Téléchargez ce tampon en tant que partie , stockez l'ETag (lisez les documents sur celui-ci).
  • Une fois que vous avez atteint EOF de vos données, téléchargez le dernier morceau (qui peut être inférieur à 5 Mo).
  • Finalisez le téléchargement en plusieurs parties.

S3 permet jusqu'à 10 000 pièces. Ainsi, en choisissant une taille de pièce de 5 Mo, vous pourrez télécharger des fichiers dynamiques jusqu'à 50 Go. Devrait être suffisant pour la plupart des cas d'utilisation.

Cependant: si vous en avez besoin de plus, vous devez augmenter la taille de votre pièce. Soit en utilisant une taille de pièce plus élevée (10 Mo par exemple), soit en l'augmentant lors du téléchargement.

First 25 parts:   5MiB (total:  125MiB)
Next 25 parts:   10MiB (total:  375MiB)
Next 25 parts:   25MiB (total:    1GiB)
Next 25 parts:   50MiB (total: 2.25GiB)
After that:     100MiB

Cela vous permettra de télécharger des fichiers jusqu'à 1 To (la limite de S3 pour un seul fichier est de 5 To en ce moment) sans gaspiller la mémoire inutilement.


Une note sur votre lien vers le blog de Sean O'Donnells :

Son problème est différent du vôtre - il connaît et utilise la longueur de contenu avant le téléchargement. Il souhaite améliorer cette situation: de nombreuses bibliothèques gèrent les téléchargements en chargeant toutes les données d'un fichier dans la mémoire. En pseudo-code, ce serait quelque chose comme ceci:

data = File.read(file_name)
request = new S3::PutFileRequest()
request.setHeader('Content-Length', data.size)
request.setBody(data)
request.send()

Sa solution le fait en obtenant le Content-Length via l'API du système de fichiers. Il diffuse ensuite les données du disque dans le flux de demandes. En pseudo-code:

upload = new S3::PutFileRequestStream()
upload.writeHeader('Content-Length', File.getSize(file_name))
upload.flushHeader()

input = File.open(file_name, File::READONLY_FLAG)

while (data = input.read())
  input.write(data)
end

upload.flush()
upload.close()
60
Marcel Jackwerth

Mettre cette réponse ici pour les autres au cas où cela aiderait:

Si vous ne connaissez pas la longueur des données que vous diffusez sur S3, vous pouvez utiliser S3FileInfo Et sa méthode OpenWrite() pour écrire des données arbitraires dans S3.

var fileInfo = new S3FileInfo(amazonS3Client, "MyBucket", "streamed-file.txt");

using (var outputStream = fileInfo.OpenWrite())
{
    using (var streamWriter = new StreamWriter(outputStream))
    {
        streamWriter.WriteLine("Hello world");
        // You can do as many writes as you want here
    }
}
5
mwrichardson

Vous pouvez utiliser l'outil de ligne de commande gof3r pour simplement diffuser des canaux Linux:

$ tar -czf - <my_dir/> | gof3r put --bucket <s3_bucket> --key <s3_object>
5
webwurst

Reportez-vous aux demandes d'entités en plusieurs parties HTTP. Vous pouvez envoyer un fichier sous forme de blocs de données à la cible.

1
Kris

Si vous utilisez Node.js, vous pouvez utiliser un plugin comme s3-streaming-upload pour accomplir cela assez facilement.

1
nathanpeck