64 votes

Node.js & Amazon S3 : Comment itérer à travers tous les fichiers dans un seau ?

Existe-t-il une bibliothèque client Amazon S3 pour Node.js qui permet de lister tous les fichiers dans le seau S3 ?

Les plus connus aws2js y knox ne semblent pas disposer de cette fonctionnalité.

0 votes

Je demanderais à l'auteur s'il peut l'implémenter dans aws2js. Je pense que ce serait très facile à faire et il a été récemment actif dans le projet. Ou, si vous en êtes capable, mettez-le en œuvre vous-même.

0 votes

Vous pouvez également mettre en œuvre cette demande spécifique par le biais de leur API REST jusqu'à ce qu'il y ait un soutien dans l'une des bibliothèques.

83voto

Meekohi Points 646

En utilisant l'outil officiel aws-sdk :

var allKeys = [];
function listAllKeys(marker, cb)
{
  s3.listObjects({Bucket: s3bucket, Marker: marker}, function(err, data){
    allKeys.push(data.Contents);

    if(data.IsTruncated)
      listAllKeys(data.NextMarker, cb);
    else
      cb();
  });
}

voir s3.listObjects

Edition 2017 : Même idée de base, mais listObjectsV2( ... ) est maintenant recommandé et utilise un ContinuationToken (voir s3.listObjectsV2 ):

var allKeys = [];
function listAllKeys(token, cb)
{
  var opts = { Bucket: s3bucket };
  if(token) opts.ContinuationToken = token;

  s3.listObjectsV2(opts, function(err, data){
    allKeys = allKeys.concat(data.Contents);

    if(data.IsTruncated)
      listAllKeys(data.NextContinuationToken, cb);
    else
      cb();
  });
}

29voto

nkitku Points 76

Utilisation du générateur asynchrone

Importation S3

const { S3 } = require("aws-sdk");
const s3 = new S3();

créer une fonction de générateur pour récupérer la liste de tous les fichiers

async function* listAllKeys(opts) {
  opts = { ...opts };
  do {
    const data = await s3.listObjectsV2(opts).promise();
    opts.ContinuationToken = data.NextContinuationToken;
    yield data;
  } while (opts.ContinuationToken);
}

Préparer le paramètre aws, sur la base de docs api

const opts = {
  Bucket: "bucket-xyz" /* required */,
  // ContinuationToken: 'STRING_VALUE',
  // Delimiter: 'STRING_VALUE',
  // EncodingType: url,
  // FetchOwner: true || false,
  // MaxKeys: 'NUMBER_VALUE',
  // Prefix: 'STRING_VALUE',
  // RequestPayer: requester,
  // StartAfter: 'STRING_VALUE'
};

Utiliser un générateur

async function main() {
  // using for of await loop
  for await (const data of listAllKeys(opts)) {
    console.log(data.Contents);
  }
}
main();

C'est tout.

Ou Lazy Load

async function main() {
  const keys = listAllKeys(opts);
  console.log(await keys.next());
  // {value: {…}, done: false}
  console.log(await keys.next());
  // {value: {…}, done: false}
  console.log(await keys.next());
  // {value: undefined, done: true}
}
main();

Ou utiliser un générateur pour créer une fonction Observable

const lister = (opts) => (o$) => {
  let needMore = true;
  const process = async () => {
    for await (const data of listAllKeys(opts)) {
      o$.next(data);
      if (!needMore) break;
    }
    o$.complete();
  };
  process();
  return () => (needMore = false);
};

utiliser cette fonction observable avec RXJS

// Using Rxjs

const { Observable } = require("rxjs");
const { flatMap } = require("rxjs/operators");

function listAll() {
  return Observable.create(lister(opts))
    .pipe(flatMap((v) => v.Contents))
    .subscribe(console.log);
}

listAll();

ou utiliser cette fonction observable avec Nodejs EventEmitter

const EventEmitter = require("events");

const _eve = new EventEmitter();

async function onData(data) {
  // will be called for each set of data
  console.log(data);
}
async function onError(error) {
  // will be called if any error
  console.log(error);
}
async function onComplete() {
  // will be called when data completely received
}
_eve.on("next", onData);
_eve.on("error", onError);
_eve.on("complete", onComplete);

const stop = lister(opts)({
  next: (v) => _eve.emit("next", v),
  error: (e) => _eve.emit("error", e),
  complete: (v) => _eve.emit("complete", v),
});

Utilisation de Typescript et AWS-SDK v3 + Deno

import {
  paginateListObjectsV2,
  S3Client,
  S3ClientConfig,
} from "@aws-sdk/client-s3";

/* // For Deno
import {
  paginateListObjectsV2,
  S3Client,
  S3ClientConfig,
} from "https://deno.land/x/aws_sdk@v3.14.0.0/client-s3/mod.ts"; */

const s3Config: S3ClientConfig = {
  credentials: {
    accessKeyId: "accessKeyId",
    secretAccessKey: "secretAccessKey",
  },
  region: "us-east-1",
};

const client = new S3Client(s3Config);
const s3Opts = { Bucket: "bucket-xyz" };

async function getAllS3Files() {
  const totalFiles = [];
  for await (const data of paginateListObjectsV2({ client }, s3Opts)) {
    totalFiles.push(...(data.Contents ?? []));
  }
  return totalFiles;
}

16voto

Ken Lin Points 86

Voici le code Node que j'ai écrit pour assembler les objets S3 à partir de listes tronquées.

var params = {
    Bucket: <yourbucket>,
    Prefix: <yourprefix>,
};

var s3DataContents = [];    // Single array of all combined S3 data.Contents

function s3Print() {
    if (program.al) {
        // --al: Print all objects
        console.log(JSON.stringify(s3DataContents, null, "    "));
    } else {
        // --b: Print key only, otherwise also print index 
        var i;
        for (i = 0; i < s3DataContents.length; i++) {
            var head = !program.b ? (i+1) + ': ' : '';
            console.log(head + s3DataContents[i].Key);
        }
    }
}

function s3ListObjects(params, cb) {
    s3.listObjects(params, function(err, data) {
        if (err) {
            console.log("listS3Objects Error:", err);
        } else {
            var contents = data.Contents;
            s3DataContents = s3DataContents.concat(contents);
            if (data.IsTruncated) {
                // Set Marker to last returned key
                params.Marker = contents[contents.length-1].Key;
                s3ListObjects(params, cb);
            } else {
                cb();
            }
        }
    });
}

s3ListObjects(params, s3Print);

Faites attention à de listObject la documentation de NextMarker, qui est PAS toujours présent dans l'objet de données retourné, donc je ne l'utilise pas du tout dans le code ci-dessus ...

NextMarker - (Chaîne) Quand la réponse est tronquée (le IsTruncated dans la réponse est vrai), vous pouvez utiliser le nom de la clé dans la fonction ce champ comme marqueur dans la requête suivante pour obtenir le prochain ensemble d'objets. Amazon S3 liste les objets par ordre alphabétique. est renvoyé uniquement si vous avez spécifié le paramètre de requête delimiter est spécifié. Si la réponse n'inclut pas le NextMarker et qu'il est tronquée, vous pouvez utiliser la valeur de la dernière Clé dans la réponse comme comme marqueur dans la requête suivante pour obtenir le prochain ensemble de clés .

L'ensemble du programme a été repoussé à https://github.com/kenklin/s3list .

9voto

nab Points 1449

En effet aws2js prend en charge l'énumération des objets d'un seau à un niveau bas via s3.get() appel de méthode. Pour ce faire, il faut passer prefix qui est documenté sur Page API REST Amazon S3 :

var s3 = require('aws2js').load('s3', awsAccessKeyId, awsSecretAccessKey);    
s3.setBucket(bucketName);

var folder = encodeURI('some/path/to/S3/folder');
var url = '?prefix=' + folder;

s3.get(url, 'xml', function (error, data) {
    console.log(error);
    console.log(data);
});

El data dans le snippet ci-dessus contient une liste de tous les objets dans le fichier bucketName seau.

6voto

hurrymaplelad Points 2522

Publié sur copie knox quand je ne trouvais pas de bonne solution existante. Enveloppe tous les détails de pagination de l'API Rest dans un flux de nœuds familier :

var knoxCopy = require('knox-copy');

var client = knoxCopy.createClient({
  key: '<api-key-here>',
  secret: '<secret-here>',
  bucket: 'mrbucket'
});

client.streamKeys({
  // omit the prefix to list the whole bucket
  prefix: 'buckets/of/fun' 
}).on('data', function(key) {
  console.log(key);
});

Si vous répertoriez moins de 1000 fichiers, une seule page suffira :

client.listPageOfKeys({
  prefix: 'smaller/bucket/o/fun'
}, function(err, page) {
  console.log(page.Contents); // <- Here's your list of files
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X