Exporter des fichiers vers Amazon S3 en Node.js : guide complet
Utilisez PutObjectCommand pour un envoi simple et Upload de @aws-sdk/lib-storage pour les envois
multipart. L’outil CLI ci-dessous exporte un fichier local vers Amazon S3, télécharge l’objet obtenu
et vérifie son SHA-256, sa taille et ses métadonnées avant de signaler la réussite. Les deux modes
d’envoi renvoient un code de sortie d’échec lorsqu’un envoi ou une vérification échoue.
Prérequis
Il vous faut Bash sous Linux, Node.js, Corepack installé séparément et un bucket S3 existant, privé et à usage général. Cet exemple a été testé avec Node.js 24.21.0 et 26.8.1, Corepack 0.36.0, Yarn 4.12.0 et les paquets AWS SDK 3.1149.0. Utilisez une version maintenue de Node.js 24 LTS ou 26 ; il s’agit de versions testées, et non d’un minimum intrinsèque du SDK. Node.js 26 n’intègre pas Corepack.
Utilisez un fichier local ordinaire et ne modifiez pas son contenu avant la fin de la vérification.
Placez son chemin entre guillemets lorsque vous appelez l’outil CLI. Les espaces, les signes de
pourcentage littéraux et les caractères Unicode des plans supplémentaires sont pris en charge. Les
chemins contenant le caractère de remplacement Unicode U+FFFD sont refusés, y compris un nom de
fichier légitime contenant ce caractère. Node.js peut remplacer par ce caractère les octets
invalides des arguments natifs, ce qui rend deux noms de fichiers différents impossibles à
distinguer. Renommez ces fichiers avant d’utiliser cet outil CLI.
Choisissez une clé d’objet inutilisée sous un préfixe qui vous appartient. Cet exemple refuse de
remplacer un objet existant. Les identifiants doivent disposer de s3:PutObject pour les envois,
de s3:GetObject pour la vérification et de s3:AbortMultipartUpload pour le nettoyage après un échec d’envoi multipart.
Configurer les identifiants AWS
Le SDK utilise sa chaîne de fournisseurs d’identifiants par défaut.
Utilisez localement un profil AWS existant et authentifié, ou un rôle IAM sur AWS. Les identifiants
temporaires fournis par des variables d’environnement fonctionnent aussi ; ils nécessitent
AWS_SESSION_TOKEN en plus de la clé d’accès et de la clé secrète. Ne placez pas d’identifiants dans le
programme et ne les versionnez pas avec le projet.
Les commandes ci-dessous lisent la région du bucket dans AWS_REGION. Pour un profil partagé nommé,
définissez AWS_PROFILE dans le shell qui exécute les commandes. Si les identifiants expirent,
renouvelez-les selon votre procédure d’authentification habituelle avant de réessayer. L’exemple ne
crée pas de bucket et ne configure pas IAM.
Installer et utiliser l’AWS SDK
Collez ce bloc dans le répertoire où vous souhaitez créer un nouveau projet s3-export. Il vérifie
chaque exécutable avant de créer des fichiers et s’exécute dans un sous-shell ; il laisse donc
inchangés le répertoire et les options de votre shell appelant. Un répertoire s3-export existant
interrompt la configuration avant qu’aucun fichier ne soit écrasé.
(
set -eu
command -v node >/dev/null || { printf 'Install Node.js first.\n' >&2; exit 1; }
command -v corepack >/dev/null || { printf 'Install Corepack first.\n' >&2; exit 1; }
mkdir s3-export
cd s3-export
cat > package.json <<'JSON'
{
"name": "s3-export",
"private": true,
"type": "module",
"packageManager": "yarn@4.12.0",
"dependencies": {
"@aws-sdk/client-s3": "3.1149.0",
"@aws-sdk/lib-storage": "3.1149.0"
}
}
JSON
touch yarn.lock
corepack yarn install
)
Le manifeste et le fichier de verrouillage locaux donnent à ce projet enfant ses propres limites de
paquet et de module, même à l’intérieur d’un projet npm ou Yarn. Yarn lit toutefois les paramètres
.yarnrc.yml applicables du répertoire personnel et des répertoires parents. Vérifiez la version
effective avec corepack yarn --version depuis s3-export ; elle devrait être 4.12.0. Si une politique héritée
(yarnPath, registre ou réseau) empêche l’installation, résolvez-la avec le responsable du projet ou
utilisez un répertoire situé hors de cette configuration. Ne la contournez pas en silence.
Si l’installation échoue après la création du projet, conservez les fichiers et relancez l’installation depuis le shell appelant :
(cd s3-export && corepack yarn install)
Enregistrez ce programme complet sous le nom s3-export/export.ts :
import { createHash } from 'node:crypto'
import { createReadStream } from 'node:fs'
import { stat } from 'node:fs/promises'
import { Readable } from 'node:stream'
import {
GetObjectCommand,
PutObjectCommand,
S3Client,
S3ServiceException,
type PutObjectCommandInput,
} from '@aws-sdk/client-s3'
import { Upload } from '@aws-sdk/lib-storage'
class ReaderError extends Error {}
async function digest(source: AsyncIterable<Uint8Array>): Promise<{ bytes: number; sha256: string }> {
const hash = createHash('sha256')
let bytes = 0
for await (const chunk of source) {
bytes += chunk.byteLength
hash.update(chunk)
}
return { bytes, sha256: hash.digest('hex') }
}
async function main(): Promise<{ bucket: string; key: string; bytes: number; sha256: string }> {
const [mode, file, bucket, key, ...extra] = process.argv.slice(2)
if (!['put', 'multipart', 'verify'].includes(mode ?? '') || !file || !bucket || !key || extra.length) {
throw new ReaderError('Usage: export.ts put|multipart|verify FILE BUCKET KEY')
}
if (file.includes('\uFFFD')) {
throw new ReaderError('FILE must not contain U+FFFD; rename the file before exporting it.')
}
const region = process.env.AWS_REGION
if (!region) throw new ReaderError('Set AWS_REGION to the bucket’s region.')
const info = await stat(file)
if (!info.isFile()) throw new ReaderError('FILE must be a regular file.')
const expected = await digest(createReadStream(file))
if (expected.bytes !== info.size) throw new ReaderError('The input changed while being read.')
const client = new S3Client({ region, maxAttempts: 1 })
try {
if (mode !== 'verify') {
const body = createReadStream(file)
try {
const params: PutObjectCommandInput = {
Bucket: bucket,
Key: key,
Body: body,
ContentLength: expected.bytes,
ContentType: 'application/octet-stream',
Metadata: { sha256: expected.sha256 },
ServerSideEncryption: 'AES256',
IfNoneMatch: '*',
}
if (mode === 'put') {
await client.send(new PutObjectCommand(params))
} else {
const upload = new Upload({
client,
params,
queueSize: 2,
leavePartsOnError: false,
})
upload.on('httpUploadProgress', ({ loaded, total }) => {
console.error(`Transferred ${loaded ?? 0}/${total ?? expected.bytes} bytes`)
})
await upload.done()
}
} finally {
body.destroy()
}
}
const stored = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }))
if (!(stored.Body instanceof Readable)) throw new ReaderError('No readable S3 response body.')
const actual = await digest(stored.Body)
if (
actual.bytes !== expected.bytes ||
actual.sha256 !== expected.sha256 ||
stored.ContentLength !== expected.bytes ||
stored.Metadata?.sha256 !== expected.sha256 ||
stored.ContentType !== 'application/octet-stream' ||
stored.ServerSideEncryption !== 'AES256'
) {
throw new ReaderError('Stored bytes or metadata do not match the input.')
}
return { bucket, key, ...actual }
} finally {
client.destroy()
}
}
main()
.then((outcome) => console.log(JSON.stringify(outcome)))
.catch((error: unknown) => {
if (error instanceof ReaderError) {
console.error(error.message)
} else {
const reason = error instanceof S3ServiceException ? error.name : 'LocalError'
console.error(`Export failed (${reason}). Check the file, region, credentials, and permissions.`)
}
process.exitCode = 1
})
Exécutez ce qui suit depuis le répertoire contenant s3-export. Remplacez la région, le bucket et
la clé par votre propre destination. Lors du premier collage, le bloc crée sample.txt et lance
l’envoi. Si ce fichier local existe déjà, Bash refuse de l’écraser : le bloc se termine avec un code
de sortie non nul, préserve le contenu du fichier local existant et ignore la commande d’envoi.
(
cd s3-export &&
(set -C; printf 'S3 export example\n' > sample.txt) &&
AWS_REGION=us-east-1 corepack yarn node export.ts put sample.txt YOUR_BUCKET exports/sample.txt
)
Pour envoyer l’échantillon existant vers une nouvelle clé, exécutez uniquement la dernière commande
du bloc depuis s3-export, en remplaçant exports/sample.txt par une clé inutilisée. Si un envoi précédent a
pu réussir, utilisez d’abord la commande verify pour inspecter sa clé
d’origine sans renvoyer le fichier.
Une exécution réussie affiche un JSON contenant le bucket, la clé, le nombre d’octets et le SHA-256 exacts. La vérification relit l’objet à partir de cette clé ; elle ne considère ni un ETag ni la réponse d’envoi comme une somme de contrôle du fichier local. Le téléchargement coûte une requête supplémentaire et relit l’intégralité des octets.
Gérer les fichiers volumineux avec l’envoi multipart
L’utilitaire multipart
découpe un flux en parties mises en mémoire tampon. Pour une entrée de longueur connue, il
choisit une taille de partie d’au moins 5 MiB, ajustée en fonction du
nombre maximal de parties. queueSize: 2 autorise deux envois de parties simultanés ; le fichier
entier n’est pas chargé en mémoire. Les petites entrées, y compris les fichiers vides, utilisent
plutôt un seul PUT.
Utilisez le même programme, avec multipart à la place de put :
(
cd s3-export &&
AWS_REGION=us-east-1 corepack yarn node export.ts multipart /path/to/large-file.zip YOUR_BUCKET exports/large-file.zip
)
La progression est affichée sur stderr. Même un événement de progression indiquant que tous les
octets ont été transférés ne signifie pas que l’envoi est terminé : le programme attend
upload.done(), puis vérifie l’objet stocké. leavePartsOnError: false demande à l’utilitaire d’abandonner l’envoi après
l’échec de l’envoi d’une partie. Dans cette version épinglée, une erreur renvoyée par
CompleteMultipartUpload,
y compris une collision de clé, laisse l’envoi inachevé en place. Demandez au propriétaire du bucket
d’inspecter et d’abandonner cet envoi ; interrompre le processus peut également laisser des parties
en place.
S3 autorise jusqu’à 5 GB en un seul PUT et jusqu’à 50 TB par envoi multipart, mais l’utilitaire JavaScript et son nombre maximal de parties ne permettent pas d’affirmer la prise en charge de toutes les tailles maximales du service. Pour les objets de plus de 5 TB, le guide d’envoi d’AWS vous oriente vers le S3 Transfer Manager en Java, en Python ou via l’AWS CLI. Ce tutoriel vérifie le comportement des envois simples et multipart avec de petits fichiers de test ; il ne constitue pas une recette pour transférer 50 TB.
Bonnes pratiques de sécurité
Gardez le bucket privé et limitez l’accès au préfixe prévu. Pour les opérations d’envoi et de vérification de ce programme, ainsi que pour l’abandon d’envois multipart par l’utilitaire, un administrateur peut accorder cette politique au niveau des objets, en remplaçant le bucket et le préfixe :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:AbortMultipartUpload"],
"Resource": "arn:aws:s3:::YOUR_BUCKET/exports/*"
}
]
}
Ces autorisations suivent les exigences des opérations multipart
et les exigences de GetObject.
Les politiques de bucket peuvent tout de même refuser une requête. Le programme utilise HTTPS et
demande explicitement le chiffrement géré par S3 avec AES256 ; il ne demande pas d’ACL publique.
Un bucket qui exige SSE-KMS nécessite une autre configuration de chiffrement et les autorisations
KMS applicables ; suivez donc la politique de ce bucket plutôt que d’ajouter des autorisations
étendues à cet exemple.
Gestion des erreurs et nouvelles tentatives
La condition IfNoneMatch: '*'
refuse une clé existante lors de l’écriture finale de l’envoi. Une collision entraîne un échec et
préserve l’objet existant ; utilisez une nouvelle clé plutôt que de supprimer les données de
quelqu’un d’autre. Les deux modes d’envoi échouent également si les identifiants sont refusés ou si
le fichier demandé est introuvable.
maxAttempts: 1 configure le SDK pour une seule tentative par requête. Ce paramètre compte la requête
initiale ; une valeur plus élevée permet donc de nouvelles tentatives. Un flux de PUT unique déjà
consommé ne peut pas simplement être rejoué ; l’utilitaire multipart met les parties en mémoire
tampon, ce qui permet au SDK de réessayer ces requêtes de partie. Si vous relancez tout l’outil CLI,
il ouvre un nouveau flux. Après un échec d’envoi, l’état de l’objet stocké peut être incertain, et
un échec de vérification peut survenir alors que l’envoi a réussi. Inspectez cette clé précise avant
de réessayer. Pour vérifier sans renvoyer le fichier, exécutez :
(
cd s3-export &&
AWS_REGION=us-east-1 corepack yarn node export.ts verify sample.txt YOUR_BUCKET exports/sample.txt
)
Cette commande compare les octets et les métadonnées au fichier local actuel et se termine avec un code non nul en cas de divergence. Elle ne supprime pas un objet non conforme et n’annule pas un envoi terminé.
Prise en charge de TypeScript
Node.js exécute ce fichier grâce à la suppression native des types.
La configuration déclare "type": "module", et corepack yarn node fournit le chargeur de dépendances de Yarn, y
compris dans les projets Plug’n’Play. Aucun tsx, aucun ts-node ni aucune étape de compilation
n’est nécessaire. L’exécution native ne vérifie pas les types : PutObjectCommandInput aide votre éditeur à
contrôler les champs des requêtes, tandis que les vérifications à l’exécution valident toujours les
arguments et les résultats stockés.
Conclusion
Considérez le JSON affiché comme le résultat d’un envoi et d’une vérification terminés, et gardez stderr à portée de main pour diagnostiquer un échec. Si l’exportation n’est qu’une étape d’un flux de traitement de fichiers plus large, découvrez nos services.
