web-dev-qa-db-fra.com

Télécharger un fichier sur Amazon S3 avec NodeJS

J'ai rencontré un problème en essayant de télécharger un fichier dans mon compartiment S3. Tout fonctionne sauf que mes paramètres de fichier ne semblent pas appropriés. J'utilise Amazon S3 sdk pour télécharger de nodejs vers s3.

Voici les paramètres de mes itinéraires:

var multiparty = require('connect-multiparty'),
    multipartyMiddleware = multiparty();
app.route('/api/items/upload').post(multipartyMiddleware, items.upload);

C'est la fonction items.upload ():

exports.upload = function(req, res) {
    var file = req.files.file;
    var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
    s3bucket.createBucket(function() {
        var params = {
            Key: file.name,
            Body: file
        };
        s3bucket.upload(params, function(err, data) {
            console.log("PRINT FILE:", file);
            if (err) {
                console.log('ERROR MSG: ', err);
            } else {
                console.log('Successfully uploaded data');
            }
        });
    });
};

Définir Body param sur une chaîne telle que "hello" fonctionne correctement. Selon doc , Body param doit prendre (Buffer, Typed Array, Blob, String, ReadableStream) Données d'objet. Cependant, le téléchargement d'un objet de fichier échoue avec le message d'erreur suivant:

[Error: Unsupported body payload object]

Ceci est l'objet fichier:

{ fieldName: 'file',
  originalFilename: 'second_fnp.png',
  path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
  headers: 
   { 'content-disposition': 'form-data; name="file"; filename="second_fnp.png"',
     'content-type': 'image/png' },
  ws: 
   { _writableState: 
      { highWaterMark: 16384,
        objectMode: false,
        needDrain: true,
        ending: true,
        ended: true,
        finished: true,
        decodeStrings: true,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        buffer: [],
        errorEmitted: false },
     writable: true,
     domain: null,
     _events: { error: [Object], close: [Object] },
     _maxListeners: 10,
     path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
     fd: null,
     flags: 'w',
     mode: 438,
     start: undefined,
     pos: undefined,
     bytesWritten: 261937,
     closed: true },
  size: 261937,
  name: 'second_fnp.png',
  type: 'image/png' }

Toute aide est la bienvenue!

48
Maximus S

Il semble donc que quelques problèmes ne se passent pas bien ici. D'après votre message, il semblerait que vous tentiez de prendre en charge le téléchargement de fichiers à l'aide du middleware connect-multiparty. Ce middleware prend le fichier téléchargé, l'écrit dans le système de fichiers local, puis définit req.files sur le (s) fichier (s) téléchargé (s).

La configuration de votre itinéraire semble correcte, le problème semble provenir de votre fonction items.upload(). En particulier avec cette partie:

var params = {
  Key: file.name,
  Body: file
};

Comme je l'ai mentionné au début de ma réponse, connect-multiparty écrit le fichier sur le système de fichiers local. Vous devez donc l'ouvrir et le lire, puis le télécharger, puis le supprimer sur le système de fichiers local.

Cela dit, vous pouvez mettre à jour votre méthode à quelque chose comme ce qui suit:

var fs = require('fs');
exports.upload = function (req, res) {
    var file = req.files.file;
    fs.readFile(file.path, function (err, data) {
        if (err) throw err; // Something went wrong!
        var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
        s3bucket.createBucket(function () {
            var params = {
                Key: file.originalFilename, //file.name doesn't exist as a property
                Body: data
            };
            s3bucket.upload(params, function (err, data) {
                // Whether there is an error or not, delete the temp file
                fs.unlink(file.path, function (err) {
                    if (err) {
                        console.error(err);
                    }
                    console.log('Temp File Delete');
                });

                console.log("PRINT FILE:", file);
                if (err) {
                    console.log('ERROR MSG: ', err);
                    res.status(500).send(err);
                } else {
                    console.log('Successfully uploaded data');
                    res.status(200).end();
                }
            });
        });
    });
};

Cela permet de lire le fichier téléchargé à partir du système de fichiers local, de le télécharger ensuite vers S3, puis de supprimer le fichier temporaire et d'envoyer une réponse.

Il y a quelques problèmes avec cette approche. Tout d’abord, ce n’est pas aussi efficace qu’il pourrait être, car pour les gros fichiers, vous allez charger tout le fichier avant de l’écrire. Deuxièmement, ce processus ne prend pas en charge les téléchargements en plusieurs parties pour les fichiers volumineux (je pense que la limite est de 5 Mo avant de devoir effectuer un téléchargement en plusieurs parties).

Ce que je suggérerais à la place, c’est que vous utilisiez un module sur lequel j’ai travaillé, appelé S3FS , qui fournit une interface similaire à la FS native dans Node.JS, mais résume certains détails tels que comme le téléchargement en plusieurs parties et l’API S3 (et ajoute des fonctionnalités supplémentaires telles que les méthodes récursives).

Si vous deviez insérer la bibliothèque S3FS , votre code ressemblerait à ceci:

var fs = require('fs'),
    S3FS = require('s3fs'),
    s3fsImpl = new S3FS('mybucketname', {
        accessKeyId: XXXXXXXXXXX,
        secretAccessKey: XXXXXXXXXXXXXXXXX
    });

// Create our bucket if it doesn't exist
s3fsImpl.create();

exports.upload = function (req, res) {
    var file = req.files.file;
    var stream = fs.createReadStream(file.path);
    return s3fsImpl.writeFile(file.originalFilename, stream).then(function () {
        fs.unlink(file.path, function (err) {
            if (err) {
                console.error(err);
            }
        });
        res.status(200).end();
    });
};

Cela va instancier le module pour le compartiment fourni et les informations d'identification AWS, puis créer le compartiment s'il n'existe pas. Ensuite, lorsqu'une demande d'envoi d'un fichier arrive, nous ouvrons un flux dans le fichier et nous l'utilisons pour écrire le fichier sur S3 dans le chemin spécifié. Cela gérera le téléchargement en plusieurs parties dans les coulisses (si nécessaire) et présente l'avantage d'être effectué par le biais d'un flux. Vous n'avez donc pas besoin d'attendre pour lire le fichier en entier avant de commencer à le télécharger.

Si vous préférez, vous pouvez modifier le code en rappels à partir de Promises . Ou utilisez la méthode pipe () avec l'écouteur d'événements pour déterminer la fin/les erreurs.

Si vous recherchez des méthodes supplémentaires, consultez la documentation de s3fs et n'hésitez pas à ouvrir un problème si vous recherchez des méthodes supplémentaires ou si vous rencontrez des problèmes.

86
David

J'ai trouvé ce qui suit être une solution de travail: 

npm install aws-sdk


Une fois que vous avez installé le aws-sdk, utilisez le code suivant en remplaçant les valeurs par vos coordonnées. 

var AWS = require('aws-sdk');
var fs =  require('fs');

var s3 = new AWS.S3();

// Bucket names must be unique across all S3 users

var myBucket = 'njera';

var myKey = 'jpeg';
//for text file
//fs.readFile('demo.txt', function (err, data) {
//for Video file
//fs.readFile('demo.avi', function (err, data) {
//for image file                
fs.readFile('demo.jpg', function (err, data) {
  if (err) { throw err; }



     params = {Bucket: myBucket, Key: myKey, Body: data };

     s3.putObject(params, function(err, data) {

         if (err) {

             console.log(err)

         } else {

             console.log("Successfully uploaded data to myBucket/myKey");

         }

      });

});

J'ai trouvé le tutoriel complet sur le sujet ici au cas où vous cherchiez des références ::


Comment télécharger des fichiers (texte/image/vidéo) dans Amazon s3 à l'aide de node.js

11
Rishabh.IO

Ou en utilisant des promesses:

const AWS = require('aws-sdk');
  AWS.config.update({
    accessKeyId: 'accessKeyId',
    secretAccessKey: 'secretAccessKey',
    region: 'region'
  });

let params = {
        Bucket: "yourBucketName",
        Key: 'someUniqueKey',
        Body: 'someFile'
      };
      try {
        let uploadPromise = await new AWS.S3().putObject(params).promise();
        console.log("Successfully uploaded data to bucket");
      } catch (e) {
        console.log("Error uploading data: ", e);
      }
3
milosnkb
var express = require('express')

app = module.exports = express();
var secureServer = require('http').createServer(app);
secureServer.listen(3001);

var aws = require('aws-sdk')
var multer = require('multer')
var multerS3 = require('multer-s3')

    aws.config.update({
    secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    accessKeyId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    region: 'us-east-1'
    });
    s3 = new aws.S3();

   var upload = multer({
   storage: multerS3({
    s3: s3,
    dirname: "uploads",
    bucket: "Your bucket name",
    key: function (req, file, cb) {
        console.log(file);
        cb(null, "uploads/profile_images/u_" + Date.now() + ".jpg"); //use  
     Date.now() for unique file keys
    }
  })
   });

 app.post('/upload', upload.single('photos'), function(req, res, next) {

 console.log('Successfully uploaded ', req.file)

 res.send('Successfully uploaded ' + req.file.length + ' files!')

})
0
user9442236

Téléchargement d'un fichier sur AWS s3 et envoi de l'URL en réponse pour accéder au fichier.

Multer est un middleware node.js pour la gestion de multipart/form-data, principalement utilisé pour le téléchargement de fichiers. Il est écrit sur busboy pour une efficacité maximale. vérifiez ce module npm ici .

Lorsque vous envoyez la demande, assurez-vous que les en-têtes, Content-Type est multipart/form-data . Nous envoyons l'emplacement du fichier dans la réponse, qui donnera l'URL, mais si vous voulez accéder à cette URL , rendez le seau public ou vous ne pourrez pas y accéder.

upload.router.js

const express = require('express');
const router = express.Router();
const AWS = require('aws-sdk');
const multer = require('multer');
const storage = multer.memoryStorage()
const upload = multer({storage: storage});

const s3Client = new AWS.S3({
    accessKeyId: 'your_access_key_id',
    secretAccessKey: 'your_secret_access_id',
    region :'ur region'
});

const uploadParams = {
         Bucket: 'ur_bucket_name', 
         Key: '', // pass key
         Body: null, // pass file body
};


router.post('/api/file/upload', upload.single("file"),(req,res) => {
    const params = uploadParams;

    uploadParams.Key = req.file.originalname;
    uploadParams.Body = req.file.buffer;

    s3Client.upload(params, (err, data) => {
        if (err) {
            res.status(500).json({error:"Error -> " + err});
        }
        res.json({message: 'File uploaded successfully','filename': 
        req.file.originalname, 'location': data.Location});
    });
});

module.exports = router;

app.js

const express = require('express');
const app = express();

const router = require('./app/routers/upload.router.js');
app.use('/', router);

// Create a Server
  const server = app.listen(8080, () => {
  console.log("App listening at 8080"); 
})
0
Nayan Patel