web-dev-qa-db-fra.com

Créer un fichier Zip sur S3 à partir de fichiers sur S3 à l'aide du nœud Lambda

J'ai besoin de créer un fichier Zip composé d'une sélection de fichiers (vidéos et images) situés dans mon compartiment s3. 

Le problème pour le moment en utilisant mon code ci-dessous est que j'ai rapidement atteint la limite de mémoire sur Lambda.

async.eachLimit(files, 10, function(file, next) {
    var params = {
        Bucket: bucket, // bucket name
        Key: file.key
    };
    s3.getObject(params, function(err, data) {
        if (err) {
            console.log('file', file.key);
            console.log('get image files err',err, err.stack); // an error occurred
        } else {
            console.log('file', file.key);
            Zip.file(file.key, data.Body);
            next();
        }
    });
}, 
function(err) {
    if (err) {
        console.log('err', err);
    } else {
        console.log('Zip', Zip);
        content = Zip.generateNodeStream({
            type: 'nodebuffer',
            streamFiles:true
        });
        var params = {
            Bucket: bucket, // name of dest bucket
            Key: 'zipped/images.Zip',
            Body: content
        };
        s3.upload(params, function(err, data) {
            if (err) {
                console.log('upload Zip to s3 err',err, err.stack); // an error occurred
            } else {
                console.log(data); // successful response
            }
        });
    }
});
  • Est-ce possible d'utiliser Lambda ou devrais-je envisager une approche différente ?

  • Est-il possible d'écrire à la volée dans un fichier Zip compressé, éliminant ainsi quelque peu le problème de mémoire, ou dois-je collecter les fichiers avant la compression?

Toute aide serait très appréciée.

13
Rabona

Ok, je dois faire ça aujourd'hui et ça marche. Direct Buffer to Stream, aucun disque impliqué. Donc, la limitation de mémoire ou de disque ne sera pas un problème ici:

'use strict';

const AWS = require("aws-sdk");
AWS.config.update( { region: "eu-west-1" } );
const s3 = new AWS.S3( { apiVersion: '2006-03-01'} );

const   _archiver = require('archiver');

//This returns us a stream.. consider it as a real pipe sending fluid to S3 bucket.. Don't forget it
const streamTo = (_bucket, _key) => {
	var stream = require('stream');
	var _pass = new stream.PassThrough();
	s3.upload( { Bucket: _bucket, Key: _key, Body: _pass }, (_err, _data) => { /*...Handle Errors Here*/ } );
	return _pass;
};
      
exports.handler = async (_req, _ctx, _cb) => {
	var _keys = ['list of your file keys in s3'];
	
    var _list = await Promise.all(_keys.map(_key => new Promise((_resolve, _reject) => {
            s3.getObject({Bucket:'bucket-name', Key:_key})
                .then(_data => _resolve( { data: _data.Body, name: `${_key.split('/').pop()}` } ));
        }
    ))).catch(_err => { throw new Error(_err) } );

    await new Promise((_resolve, _reject) => { 
        var _myStream = streamTo('bucket-name', 'fileName.Zip');		//Now we instantiate that pipe...
        var _archive = _archiver('Zip');
        _archive.on('error', err => { throw new Error(err); } );
        
        //Your promise gets resolved when the fluid stops running... so that's when you get to close and resolve
        _myStream.on('close', _resolve);
        _myStream.on('end', _resolve);
        _myStream.on('error', _reject);
        
        _archive.pipe(_myStream);			//Pass that pipe to _archive so it can Push the fluid straigh down to S3 bucket
        _list.forEach(_itm => _archive.append(_itm.data, { name: _itm.name } ) );		//And then we start adding files to it
        _archive.finalize();				//Tell is, that's all we want to add. Then when it finishes, the promise will resolve in one of those events up there
    }).catch(_err => { throw new Error(_err) } );
    
    _cb(null, { } );		//Handle response back to server
};

4
user2718866

Utiliser des flux peut être délicat car je ne sais pas comment vous pouvez diriger plusieurs flux vers un objet. Je l'ai fait plusieurs fois en utilisant un objet fichier standard. C'est un processus en plusieurs étapes et c'est assez rapide. N'oubliez pas que Lambda fonctionne sous Linux, vous avez donc toutes les ressources Linux sous la main, y compris le répertoire system/tmp.

  1. Créez un sous-répertoire dans/tmp appelez "transitoire" ou tout ce qui vous convient
  2. Utilisez s3.getObject () et écrivez des objets de fichier dans/tmp/transient
  3. Utilisez le package GLOB pour générer un tableau [] de chemins à partir de/tmp/transient 
  4. Boucle le tableau et Zip.addLocalFile (tableau [i]);
  5. Zip.writeZip ('tmp/files.Zip');
1
jp_inc