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.
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
};
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.