it-swarm.com.ru

Создайте Zip-файл на S3 из файлов на S3, используя Lambda Node

Мне нужно создать Zip-файл, который состоит из набора файлов (видео и изображений), расположенных в моем контейнере s3. 

На данный момент проблема с использованием моего кода в том, что я быстро достиг предела памяти в 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
            }
        });
    }
});
  • Возможно ли это с помощью Lambda, или я должен взглянуть на другой подход ?

  • Можно ли записать сжатый Zip-файл на лету, что несколько устраняет проблему с памятью, или мне нужно собрать файлы перед сжатием?

Любая помощь приветствуется.

13
Rabona

Хорошо, я должен сделать это сегодня, и это работает. Прямой буфер в поток, диск не задействован. Таким образом, ограничение памяти или диска не будет проблемой здесь:

'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

Использование потоков может быть непростым делом, так как я не уверен, как можно передать несколько потоков в объект. Я сделал это несколько раз, используя стандартный объект файла. Это многоступенчатый процесс, и он довольно быстрый. Помните, что Lambda работает в Linux, поэтому у вас есть все ресурсы Linux, включая каталог system/tmp.

  1. Создайте в/tmp подкаталог с именем «transient» или любым другим, который вам подходит
  2. Используйте s3.getObject () и записывайте файловые объекты в/tmp/transient
  3. Используйте пакет GLOB для генерации массива [] путей из/tmp/transient 
  4. Цикл массива и Zip.addLocalFile (array [i]);
  5. Zip.writeZip ( 'TMP/files.Zip');
1
jp_inc