
Bibliothèque Node.js pour diffuser des fichiers en tant que réponses HTTP avec prise en charge du contenu partiel, des requêtes conditionnelles et des en-têtes de cache configurables. Conçue pour être utilisée dans des frameworks web comme Fastify.
# @fastify/send
[](https://github.com/fastify/send/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@fastify/send)
[](https://github.com/neostandard/neostandard)
Send est une bibliothèque pour diffuser en continu des fichiers du système de fichiers en réponse HTTP, prenant en charge les réponses partielles (Ranges), la négociation conditionnelle GET (If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since), une couverture de tests élevée et des événements granulaires qui peuvent être exploités pour prendre les mesures appropriées dans votre application ou framework.
## Installation
Ce module [Node.js](https://nodejs.org/en/) est disponible via le [registre npm](https://www.npmjs.com/). L'installation se fait à l'aide de la [commande `npm install`](https://docs.npmjs.com/downloading-and-installing-packages-locally) :
```bash
$ npm install @fastify/send
```
### TypeScript
`@types/mime@3` doit être utilisé si vous souhaitez utiliser TypeScript ;
`@types/mime@4` a supprimé les types `mime`.
```bash
$ npm install -D @types/mime@3
```
## API
```js
const send = require('@fastify/send')
```
### send(req, path, [options])
Fournit `statusCode`, `headers`, et `stream` pour le chemin donné à envoyer à un
`res`. `req` est la requête HTTP Node.js et `path` est un chemin encodé en URL
à envoyer (encodé en URL, pas le chemin réel du système de fichiers).
#### Options
##### acceptRanges
Active ou désactive l'acceptation des requêtes avec plages, par défaut true.
En désactivant cela, l'en-tête `Accept-Ranges` ne sera pas envoyé et le contenu
de l'en-tête de requête `Range` sera ignoré.
##### cacheControl
Active ou désactive l'en-tête de réponse `Cache-Control`, par défaut true.
La désactivation de cette option ignore les options `immutable` et `maxAge`.
##### contentType
Par défaut, cette bibliothèque utilise le module `mime` pour définir le `Content-Type`
de la réponse en fonction de l'extension du fichier demandé.
Pour désactiver cette fonctionnalité, définissez `contentType` à `false`.
L'en-tête `Content-Type` devra être défini manuellement si désactivé.
##### dotfiles
Définit comment les "dotfiles" sont traités lorsqu'ils sont rencontrés. Un dotfile est un fichier
ou un répertoire qui commence par un point ("."). Notez que cette vérification est effectuée sur
le chemin lui-même sans vérifier si le chemin existe sur le
disque. Si `root` est spécifié, seuls les dotfiles au-dessus de la racine sont
vérifiés (c'est-à-dire que la racine elle-même peut se trouver dans un dotfile lorsqu'elle est définie
sur "deny").
- `'allow'` Aucun traitement spécial pour les dotfiles.
- `'deny'` Envoyer un 403 pour toute requête concernant un dotfile.
- `'ignore'` Faire comme si le dotfile n'existait pas et renvoyer 404.
La valeur par défaut est _similaire_ à `'ignore'`, à l'exception que
cette valeur par défaut n'ignorera pas les fichiers dans un répertoire qui commence
par un point, pour des raisons de rétrocompatibilité.
##### end
Décalage en octets à la fin du flux, par défaut la longueur du fichier
moins 1. La fin est inclusive dans le flux, ce qui signifie que `end: 3` inclura
le 4ème octet dans le flux.
##### etag
Active ou désactive la génération d'etag, par défaut true.
##### extensions
Si un fichier donné n'existe pas, essayez d'ajouter une des extensions données,
dans l'ordre donné. Par défaut, cette option est désactivée (définie à `false`). Un
exemple de valeur qui servira des fichiers HTML sans extension : `['html', 'htm']`.
Cette option est ignorée si le fichier demandé a déjà une extension.
##### immutable
Active ou désactive la directive `immutable` dans l'en-tête de réponse `Cache-Control`,
par défaut `false`. Si défini à `true`, l'option `maxAge` doit également
être spécifiée pour activer la mise en cache. La directive `immutable` empêchera
les clients compatibles d'effectuer des requêtes conditionnelles pendant la durée de l'option
`maxAge` pour vérifier si le fichier a changé.
##### index
Par défaut, send prend en charge les fichiers "index.html", pour désactiver cela,
définissez `false` ou pour fournir un nouvel index, passez une chaîne ou un tableau
dans l'ordre préféré.
##### lastModified
Active ou désactive l'en-tête `Last-Modified`, par défaut true. Utilise la valeur
de dernière modification du système de fichiers.
##### maxAge
Fournit un âge maximum en millisecondes pour la mise en cache HTTP, par défaut 0.
Cela peut aussi être une chaîne acceptée par le module
[ms](https://www.npmjs.com/package/ms).
##### maxContentRangeChunkSize
Spécifie la taille maximale du contenu de la réponse, par défaut la taille totale du fichier.
Cela sera utilisé lorsque `acceptRanges` est true.
##### root
Sert les fichiers relatifs à `path`.
##### start
Décalage en octets au début du flux, par défaut 0. Le début est inclusif,
ce qui signifie que `start: 2` inclura le 3ème octet dans le flux.
##### highWaterMark
Lorsqu'elle est fournie, cette option définit le nombre maximum d'octets que le tampon interne
conservera avant de suspendre les lectures de la ressource sous-jacente.
Si vous omettez cette option (ou passez undefined), Node.js revient à
son paramètre par défaut intégré pour les flux binaires lisibles.
### .mime
L'export `mime` est l'instance globale du
[module npm `mime`](https://www.npmjs.com/package/mime).
Il est utilisé pour configurer les types MIME associés aux extensions de fichiers
ainsi que d'autres options pour résoudre le type MIME d'un fichier (comme le
type par défaut à utiliser pour une extension de fichier inconnue).
## Caching
Il n'effectue _pas_ de mise en cache interne, vous devriez utiliser un proxy inverse
comme Varnish pour cela, ou ces choses fantaisistes appelées CDN. Si votre
application est suffisamment petite pour bénéficier d'une mise en cache mémoire sur un seul nœud,
elle est suffisamment petite pour n'avoir pas besoin de mise en cache du tout ;).
## Débogage
Pour activer la sortie de l'instrumentation `debug()`, exportez __NODE_DEBUG__ :
```
$ NODE_DEBUG=send node app
```
## Exécution des tests
```
$ npm install
$ npm test
```
## Exemples
### Servir un fichier spécifique
Cet exemple simple enverra un fichier spécifique à toutes les requêtes.
```js
const http = require('node:http')
const send = require('send')
const server = http.createServer(async function onRequest (req, res) {
const { statusCode, headers, stream } = await send(req, '/path/to/index.html')
res.writeHead(statusCode, headers)
stream.pipe(res)
})
server.listen(3000)
```
### Servir tous les fichiers d'un répertoire
Cet exemple simple servira tous les fichiers d'un
répertoire donné au niveau supérieur. Par exemple, une requête
`GET /foo.txt` renverra `/www/public/foo.txt`.
```js
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
const server = http.createServer(async function onRequest (req, res) {
const { statusCode, headers, stream } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
res.writeHead(statusCode, headers)
stream.pipe(res)
})
server.listen(3000)
```
### Types de fichiers personnalisés
```js
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
// Types inconnus par défaut en text/plain
send.mime.default_type = 'text/plain'
// Ajouter un type personnalisé
send.mime.define({
'application/x-my-type': ['x-mt', 'x-mtt']
})
const server = http.createServer(function onRequest (req, res) {
const { statusCode, headers, stream } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
res.writeHead(statusCode, headers)
stream.pipe(res)
})
server.listen(3000)
```
### Vue d'index de répertoire personnalisée
Ceci est un exemple de service d'une structure de répertoires avec une
fonction personnalisée pour afficher la liste d'un répertoire.
```js
const http = require('node:http')
const fs = require('node:fs')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
// Transférer des fichiers arbitraires depuis /www/example.com/public/*
// avec un gestionnaire personnalisé pour l'affichage du répertoire
const server = http.createServer(async function onRequest (req, res) {
const { statusCode, headers, stream, type, metadata } = await send(req, parseUrl(req).pathname, { index: false, root: '/www/public' })
if(type === 'directory') {
// obtenir la liste du répertoire
const list = await readdir(metadata.path)
// afficher un index pour le répertoire
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' })
res.end(list.join('\n') + '\n')
} else {
res.writeHead(statusCode, headers)
stream.pipe(res)
}
})
server.listen(3000)
```
### Service depuis un répertoire racine avec gestion d'erreurs personnalisée
```js
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
const server = http.createServer(async function onRequest (req, res) {
// transférer des fichiers arbitraires depuis
// /www/example.com/public/*
const { statusCode, headers, stream, type, metadata } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
switch (type) {
case 'directory': {
// votre logique personnalisée de gestion des répertoires :
res.writeHead(301, {
'Location': metadata.requestPath + '/'
})
res.end('Redirection vers ' + metadata.requestPath + '/')
break
}
case 'error': {
// votre logique personnalisée de gestion des erreurs :
res.writeHead(metadata.error.status ?? 500, {})
res.end(metadata.error.message)
break
}
default: {
// vos en-têtes personnalisés
// servir tous les fichiers en téléchargement
res.setHeader('Content-Disposition', 'attachment')
res.writeHead(statusCode, headers)
stream.pipe(res)
}
}
})
server.listen(3000)
```
## Licence
Sous licence [MIT](https://github.com/fastify/send/blob/main/LICENSE).