
Librería de Node.js para transmitir archivos como respuestas HTTP con soporte para contenido parcial, solicitudes condicionales y cabeceras de caché configurables. Diseñada para su uso en frameworks web como 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 es una biblioteca para transmitir archivos desde el sistema de archivos como una respuesta HTTP, compatible con respuestas parciales (Ranges), negociación conditional-GET (If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since), alta cobertura de pruebas y eventos granulares que pueden aprovecharse para tomar las acciones adecuadas en tu aplicación o framework.
## Instalación
Este es un módulo de [Node.js](https://nodejs.org/en/) disponible a través del [registro npm](https://www.npmjs.com/). La instalación se realiza usando el [comando `npm install`](https://docs.npmjs.com/downloading-and-installing-packages-locally):
```bash
$ npm install @fastify/send
```
### TypeScript
Se debe usar `@types/mime@3` si se quiere utilizar TypeScript;
`@types/mime@4` eliminó los tipos de `mime`.
```bash
$ npm install -D @types/mime@3
```
## API
```js
const send = require('@fastify/send')
```
### send(req, path, [options])
Proporciona `statusCode`, `headers` y `stream` para la ruta dada que se enviará a un
`res`. El `req` es la petición HTTP de Node.js y el `path` es una ruta codificada en URL
que se enviará (codificada en URL, no la ruta real del sistema de archivos).
#### Opciones
##### acceptRanges
Habilita o deshabilita la aceptación de peticiones por rangos, por defecto es true.
Deshabilitarlo no enviará `Accept-Ranges` e ignorará el contenido
de la cabecera de petición `Range`.
##### cacheControl
Habilita o deshabilita el establecimiento de la cabecera de respuesta `Cache-Control`, por defecto es
true. Deshabilitarlo hará que se ignoren las opciones `immutable` y `maxAge`.
##### contentType
Por defecto, esta biblioteca usa el módulo `mime` para establecer el `Content-Type`
de la respuesta basándose en la extensión del archivo solicitado.
Para deshabilitar esta funcionalidad, establece `contentType` en `false`.
La cabecera `Content-Type` tendrá que establecerse manualmente si se deshabilita.
##### dotfiles
Establece cómo se tratan los "dotfiles" cuando se encuentran. Un dotfile es un archivo
o directorio que comienza con un punto ("."). Ten en cuenta que esta comprobación se realiza sobre
la ruta en sí sin comprobar si la ruta existe en el
disco. Si se especifica `root`, solo se comprueban los dotfiles por encima de la raíz
(es decir, la propia raíz puede estar dentro de un dotfile cuando se establece
en "deny").
- `'allow'` Sin tratamiento especial para los dotfiles.
- `'deny'` Envía un 403 para cualquier petición de un dotfile.
- `'ignore'` Finge que el dotfile no existe y devuelve 404.
El valor por defecto es _similar_ a `'ignore'`, con la excepción de que
este valor por defecto no ignorará los archivos dentro de un directorio que comienza
con un punto, por compatibilidad hacia atrás.
##### end
Desplazamiento en bytes en el que termina el stream, por defecto es la longitud del archivo
menos 1. El final es inclusivo en el stream, lo que significa que `end: 3` incluirá
el cuarto byte del stream.
##### etag
Habilita o deshabilita la generación de etag, por defecto es true.
##### extensions
Si un archivo dado no existe, intenta añadir una de las extensiones dadas,
en el orden dado. Por defecto, esto está deshabilitado (establecido en `false`). Un
valor de ejemplo que servirá archivos HTML sin extensión: `['html', 'htm']`.
Esto se omite si el archivo solicitado ya tiene una extensión.
##### immutable
Habilita o deshabilita la directiva `immutable` en la cabecera de respuesta
`Cache-Control`, por defecto es `false`. Si se establece en `true`, la opción `maxAge` también
debería especificarse para habilitar el almacenamiento en caché. La directiva `immutable` evitará que
los clientes compatibles realicen peticiones condicionales durante la vigencia de la
opción `maxAge` para comprobar si el archivo ha cambiado.
##### index
Por defecto send admite archivos "index.html", para deshabilitar esto
establece `false` o para proporcionar un nuevo índice pasa una cadena o un array
en el orden preferido.
##### lastModified
Habilita o deshabilita la cabecera `Last-Modified`, por defecto es true. Usa el valor
de última modificación del sistema de archivos.
##### maxAge
Proporciona un max-age en milisegundos para el almacenamiento en caché HTTP, por defecto es 0.
Esto también puede ser una cadena aceptada por el
módulo [ms](https://www.npmjs.com/package/ms).
##### maxContentRangeChunkSize
Especifica el tamaño máximo del contenido de la respuesta, por defecto es el tamaño completo del archivo.
Esto se usará cuando `acceptRanges` sea true.
##### onFile
Se llama con la ruta absoluta del archivo final seleccionado y las estadísticas del sistema de archivos
después de la resolución del archivo, pero antes del procesamiento de la respuesta y la creación del stream del archivo.
La callback puede ser síncrona o devolver una promesa. Si devuelve una promesa,
`send` espera a que se resuelva. Si la callback lanza una excepción o su promesa devuelta
se rechaza, la operación se aborta y `send()` se rechaza.
##### root
Sirve archivos relativos a `path`.
##### start
Desplazamiento en bytes en el que comienza el stream, por defecto es 0. El inicio es inclusivo,
lo que significa que `start: 2` incluirá el tercer byte del stream.
##### highWaterMark
Cuando se proporciona, esta opción establece el número máximo de bytes que el búfer
interno mantendrá antes de pausar las lecturas del recurso subyacente.
Si omites esta opción (o pasas undefined), Node.js recurre a
su valor predeterminado integrado para streams binarios legibles.
### .mime
La exportación `mime` es la instancia global del
[módulo npm `mime`](https://www.npmjs.com/package/mime).
Se utiliza para configurar los tipos MIME asociados a las extensiones de archivo,
así como otras opciones sobre cómo resolver el tipo MIME de un archivo (como el
tipo predeterminado que se usa para una extensión de archivo desconocida).
## Almacenamiento en caché
_No_ realiza almacenamiento en caché interno; para ello deberías usar una caché de proxy inverso
como Varnish, o esas cosas sofisticadas llamadas CDN. Si tu
aplicación es lo suficientemente pequeña como para beneficiarse del almacenamiento en caché en memoria de un solo nodo,
es lo suficientemente pequeña como para no necesitar almacenamiento en caché en absoluto ;).
## Depuración
Para habilitar la salida de instrumentación de `debug()`, exporta __NODE_DEBUG__:
```
$ NODE_DEBUG=send node app
```
## Ejecución de pruebas
```
$ npm install
$ npm test
```
## Ejemplos
### Servir un archivo específico
Este ejemplo sencillo enviará un archivo específico a todas las solicitudes.
```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 todos los archivos de un directorio
Este ejemplo sencillo simplemente servirá todos los archivos de un
directorio dado como nivel superior. Por ejemplo, una solicitud
`GET /foo.txt` devolverá `/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)
```
### Tipos de archivo personalizados
```js
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
// Default unknown types to text/plain
send.mime.default_type = 'text/plain'
// Add a custom type
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)
```
### Vista de índice de directorio personalizada
Este es un ejemplo de cómo servir una estructura de directorios con una
función personalizada para generar un listado de un directorio.
```js
const http = require('node:http')
const fs = require('node:fs')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
// Transfer arbitrary files from within /www/example.com/public/*
// with a custom handler for directory listing
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') {
// get directory list
const list = await readdir(metadata.path)
// render an index for the directory
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)
```
### Servir desde un directorio raíz con manejo de errores personalizado
```js
const http = require('node:http')
const parseUrl = require('parseurl')
const send = require('@fastify/send')
const server = http.createServer(async function onRequest (req, res) {
// transfer arbitrary files from within
// /www/example.com/public/*
const { statusCode, headers, stream, type, metadata } = await send(req, parseUrl(req).pathname, { root: '/www/public' })
switch (type) {
case 'directory': {
// your custom directory handling logic:
res.writeHead(301, {
'Location': metadata.requestPath + '/'
})
res.end('Redirecting to ' + metadata.requestPath + '/')
break
}
case 'error': {
// your custom error-handling logic:
res.writeHead(metadata.error.status ?? 500, {})
res.end(metadata.error.message)
break
}
default: {
// your custom headers
// serve all files for download
res.setHeader('Content-Disposition', 'attachment')
res.writeHead(statusCode, headers)
stream.pipe(res)
}
}
})
server.listen(3000)
```
## Licencia
Con licencia [MIT](https://github.com/fastify/send/blob/main/LICENSE).