Los servidores de producción a menudo leen, suben y escriben archivos en servicios de almacenamiento de objetos compatibles con S3 en lugar del sistema de archivos local. Históricamente, eso significa que las APIs del sistema de archivos locales que usas en desarrollo no se pueden usar en producción. Cuando usas Bun, las cosas son diferentes.
La API S3 de Bun es rápida
Bun proporciona bindings rápidos y nativos para interactuar con servicios de almacenamiento de objetos compatibles con S3. La API S3 de Bun está diseñada para ser simple y sentirse similar a las APIs Response y Blob de fetch (como las APIs del sistema de archivos locales de Bun).
import { s3, write, S3Client } from "bun";
// Bun.s3 lee las variables de entorno para las credenciales
// file() devuelve una referencia lazy a un archivo en S3
const metadata = s3.file("123.json");
// Descargar desde S3 como JSON
const data = await metadata.json();
// Subir a S3
await write(metadata, JSON.stringify({ name: "John", age: 30 }));
// Firmar previamente una URL (síncrono - no se necesita solicitud de red)
const url = metadata.presign({
acl: "public-read",
expiresIn: 60 * 60 * 24, // 1 día
});
// Eliminar el archivo
await metadata.delete();S3 es el estándar de facto del sistema de archivos de internet. La API S3 de Bun funciona con servicios de almacenamiento compatibles con S3 como:
- AWS S3
- Cloudflare R2
- DigitalOcean Spaces
- MinIO
- Backblaze B2
- ...y cualquier otro servicio de almacenamiento compatible con S3
Uso Básico
Hay varias formas de interactuar con la API S3 de Bun.
Bun.S3Client y Bun.s3
Bun.s3 es equivalente a new Bun.S3Client(), dependiendo de las variables de entorno para las credenciales.
Para establecer credenciales explícitamente, pásalas al constructor Bun.S3Client.
import { S3Client } from "bun";
const client = new S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// sessionToken: "..."
// acl: "public-read",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
// endpoint: "https://<region>.digitaloceanspaces.com", // DigitalOcean Spaces
// endpoint: "http://localhost:9000", // MinIO
});
// Bun.s3 es un singleton global que es equivalente a `new Bun.S3Client()`Trabajar con Archivos S3
El método file en S3Client devuelve una referencia lazy a un archivo en S3.
// Una referencia lazy a un archivo en S3
const s3file: S3File = client.file("123.json");Como Bun.file(path), el método file de S3Client es sincrónico. No realiza ninguna solicitud de red hasta que llamas a un método que depende de una solicitud de red.
Leer archivos desde S3
Si has usado la API fetch, estás familiarizado con las APIs Response y Blob. S3File extiende Blob. Los mismos métodos que funcionan en Blob también funcionan en S3File.
// Leer un S3File como texto
const text = await s3file.text();
// Leer un S3File como JSON
const json = await s3file.json();
// Leer un S3File como ArrayBuffer
const buffer = await s3file.arrayBuffer();
// Obtener solo los primeros 1024 bytes
const partial = await s3file.slice(0, 1024).text();
// Transmitir el archivo
const stream = s3file.stream();
for await (const chunk of stream) {
console.log(chunk);
}Optimización de memoria
Métodos como text(), json(), bytes(), o arrayBuffer() evitan duplicar la cadena o bytes en memoria cuando es posible.
Si el texto resulta ser ASCII, Bun transfiere directamente la cadena a JavaScriptCore (el motor) sin transcodificación y sin duplicar la cadena en memoria. Cuando usas .bytes() o .arrayBuffer(), también evitará duplicar los bytes en memoria.
Estos métodos auxiliares no solo simplifican la API, también la hacen más rápida.
Escribir y subir archivos a S3
Escribir en S3 es igual de simple.
// Escribir una cadena (reemplazando el archivo)
await s3file.write("Hello World!");
// Escribir un Buffer (reemplazando el archivo)
await s3file.write(Buffer.from("Hello World!"));
// Escribir una Response (reemplazando el archivo)
await s3file.write(new Response("Hello World!"));
// Escribir con tipo de contenido
await s3file.write(JSON.stringify({ name: "John", age: 30 }), {
type: "application/json",
});
// Escribir usando un writer (streaming)
const writer = s3file.writer({ type: "application/json" });
writer.write("Hello");
writer.write(" World!");
await writer.end();
// Escribir usando Bun.write
await Bun.write(s3file, "Hello World!");Trabajar con archivos grandes (streams)
Bun maneja automáticamente las subidas multipart para archivos grandes y proporciona capacidades de streaming. La misma API que funciona para archivos locales también funciona para archivos S3.
// Escribir un archivo grande
const bigFile = Buffer.alloc(10 * 1024 * 1024); // 10MB
const writer = s3file.writer({
// Reintentar automáticamente en errores de red hasta 3 veces
retry: 3,
// Poner en cola hasta 10 solicitudes a la vez
queueSize: 10,
// Subir en fragmentos de 5 MB
partSize: 5 * 1024 * 1024,
});
for (let i = 0; i < 10; i++) {
writer.write(bigFile);
await writer.flush();
}
await writer.end();Firmar URLs Previamente
Cuando tu servicio de producción necesita permitir que los usuarios suban archivos a tu servidor, a menudo es más fiable que el usuario suba directamente a S3 en lugar de que tu servidor actúe como intermediario.
Para facilitar esto, puedes firmar previamente URLs para archivos S3. Esto genera una URL con una firma que permite a un usuario subir de forma segura ese archivo específico a S3, sin exponer tus credenciales ni otorgarles acceso innecesario a tu bucket.
El comportamiento predeterminado es generar una URL GET que expira en 24 horas. Bun intenta inferir el tipo de contenido desde la extensión del archivo. Si la inferencia no es posible, el valor predeterminado será application/octet-stream.
import { s3 } from "bun";
// Generar una URL firmada previamente que expira en 24 horas (predeterminado)
const download = s3.presign("my-file.txt"); // GET, text/plain, expira en 24 horas
const upload = s3.presign("my-file", {
expiresIn: 3600, // 1 hora
method: "PUT",
type: "application/json", // Sin extensión para inferir, así que podemos especificar que el tipo de contenido sea JSON
});
// Puedes llamar a .presign() si tienes una referencia de archivo, pero evita hacerlo
// a menos que ya tengas una referencia (para evitar uso de memoria).
const myFile = s3.file("my-file.txt");
const presignedFile = myFile.presign({
expiresIn: 3600, // 1 hora
});Establecer ACLs
Para establecer un ACL (lista de control de acceso) en una URL firmada previamente, pasa la opción acl:
const url = s3file.presign({
acl: "public-read",
expiresIn: 3600,
});Puedes pasar cualquiera de los siguientes ACLs:
| ACL | Explicación |
|---|---|
"public-read" | El objeto es legible por el público. |
"private" | El objeto es legible solo por el propietario del bucket. |
"public-read-write" | El objeto es legible y escribible por el público. |
"authenticated-read" | El objeto es legible por el propietario del bucket y usuarios autenticados. |
"aws-exec-read" | El objeto es legible por la cuenta de AWS que realizó la solicitud. |
"bucket-owner-read" | El objeto es legible por el propietario del bucket. |
"bucket-owner-full-control" | El objeto es legible y escribible por el propietario del bucket. |
"log-delivery-write" | El objeto es escribible por servicios de AWS usados para entrega de logs. |
URLs con Expiración
Para establecer un tiempo de expiración para una URL firmada previamente, pasa la opción expiresIn.
const url = s3file.presign({
// Segundos
expiresIn: 3600, // 1 hora
// lista de control de acceso
acl: "public-read",
// método HTTP
method: "PUT",
});method
Para establecer el método HTTP para una URL firmada previamente, pasa la opción method.
const url = s3file.presign({
method: "PUT",
// method: "DELETE",
// method: "GET",
// method: "HEAD",
// method: "POST",
// method: "PUT",
});new Response(S3File)
Para redirigir rápidamente a los usuarios a una URL firmada previamente para un archivo S3, pasa una instancia S3File a un objeto Response como cuerpo.
Esto redirigirá automáticamente al usuario a la URL firmada previamente para el archivo S3, ahorrándote el costo de memoria, tiempo y ancho de banda de descargar el archivo a tu servidor y enviarlo de vuelta al usuario.
const response = new Response(s3file);
console.log(response);Response (0 KB) {
ok: false,
url: "",
status: 302,
statusText: "",
headers: Headers {
"location": "https://<account-id>.r2.cloudflarestorage.com/...",
},
redirected: true,
bodyUsed: false
}Soporte para Servicios Compatibles con S3
La implementación S3 de Bun funciona con cualquier servicio de almacenamiento compatible con S3. Solo especifica el endpoint apropiado:
Usar Bun's S3Client con AWS S3
AWS S3 es el predeterminado. También puedes pasar una opción region en lugar de una opción endpoint para AWS S3.
import { S3Client } from "bun";
// AWS S3
const s3 = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// region: "us-east-1",
});Usar Bun's S3Client con Google Cloud Storage
Para usar el cliente S3 de Bun con Google Cloud Storage, establece endpoint en "https://storage.googleapis.com" en el constructor S3Client.
import { S3Client } from "bun";
// Google Cloud Storage
const gcs = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
endpoint: "https://storage.googleapis.com",
});Usar Bun's S3Client con Cloudflare R2
Para usar el cliente S3 de Bun con Cloudflare R2, establece endpoint en el endpoint de R2 en el constructor S3Client. El endpoint de R2 incluye tu ID de cuenta.
import { S3Client } from "bun";
// CloudFlare R2
const r2 = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
endpoint: "https://<account-id>.r2.cloudflarestorage.com",
});Usar Bun's S3Client con DigitalOcean Spaces
Para usar el cliente S3 de Bun con DigitalOcean Spaces, establece endpoint en el endpoint de DigitalOcean Spaces en el constructor S3Client.
import { S3Client } from "bun";
const spaces = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
// region: "nyc3",
endpoint: "https://<region>.digitaloceanspaces.com",
});Usar Bun's S3Client con MinIO
Para usar el cliente S3 de Bun con MinIO, establece endpoint en la URL en la que se está ejecutando MinIO en el constructor S3Client.
import { S3Client } from "bun";
const minio = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
// ¡Asegúrate de usar la URL de endpoint correcta!
// ¡Podría no ser localhost en producción!
endpoint: "http://localhost:9000",
});Usar Bun's S3Client con supabase
Para usar el cliente S3 de Bun con supabase, establece endpoint en el endpoint de supabase en el constructor S3Client. El endpoint de supabase incluye tu ID de cuenta y la ruta /storage/v1/s3. Asegúrate de establecer "Habilitar conexión vía protocolo S3" en el panel de supabase en https://supabase.com/dashboard/project/<account-id>/settings/storage y establecer la región informada en la misma sección.
import { S3Client } from "bun";
const supabase = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
region: "us-west-1",
endpoint: "https://<account-id>.supabase.co/storage/v1/s3/storage",
});Usar Bun's S3Client con endpoints de estilo Virtual Hosted de S3
Cuando uses un endpoint de estilo Virtual Hosted de S3, necesitas establecer la opción virtualHostedStyle en true.
NOTE
- Si no especificas un endpoint, Bun determinará automáticamente el endpoint de AWS S3 usando la región y bucket proporcionados. - Si no se especifica ninguna región, Bun usa us-east-1 por defecto. - Si proporcionas explícitamente un endpoint, no necesitas especificar un nombre de bucket.import { S3Client } from "bun";
// Endpoint de AWS S3 inferido de la región y bucket
const s3 = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
virtualHostedStyle: true,
// endpoint: "https://my-bucket.s3.us-east-1.amazonaws.com",
// region: "us-east-1",
});
// AWS S3
const s3WithEndpoint = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
endpoint: "https://<bucket-name>.s3.<region>.amazonaws.com",
virtualHostedStyle: true,
});
// Cloudflare R2
const r2WithEndpoint = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
endpoint: "https://<bucket-name>.<account-id>.r2.cloudflarestorage.com",
virtualHostedStyle: true,
});Credenciales
Las credenciales son una de las partes más difíciles de usar S3, y hemos intentado hacerlo lo más fácil posible. Por defecto, Bun lee las siguientes variables de entorno para las credenciales.
| Nombre de opción | Variable de entorno |
|---|---|
accessKeyId | S3_ACCESS_KEY_ID |
secretAccessKey | S3_SECRET_ACCESS_KEY |
region | S3_REGION |
endpoint | S3_ENDPOINT |
bucket | S3_BUCKET |
sessionToken | S3_SESSION_TOKEN |
Si la variable de entorno S3_* no está establecida, Bun también verificará la variable de entorno AWS_* para cada una de las opciones anteriores.
| Nombre de opción | Variable de entorno de respaldo |
|---|---|
accessKeyId | AWS_ACCESS_KEY_ID |
secretAccessKey | AWS_SECRET_ACCESS_KEY |
region | AWS_REGION |
endpoint | AWS_ENDPOINT |
bucket | AWS_BUCKET |
sessionToken | AWS_SESSION_TOKEN |
Estas variables de entorno se leen desde archivos .env o desde el entorno del proceso en el momento de la inicialización (process.env no se usa para esto).
Estos valores predeterminados son anulados por las opciones que pasas a s3.file(credentials), new Bun.S3Client(credentials), o cualquiera de los métodos que aceptan credenciales. Así que si, por ejemplo, usas las mismas credenciales para diferentes buckets, puedes establecer las credenciales una vez en tu archivo .env y luego pasar bucket: "my-bucket" a la función s3.file() sin tener que especificar todas las credenciales nuevamente.
Objetos S3Client
Cuando no usas variables de entorno o usas múltiples buckets, puedes crear un objeto S3Client para establecer credenciales explícitamente.
import { S3Client } from "bun";
const client = new S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// sessionToken: "..."
endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
// endpoint: "http://localhost:9000", // MinIO
});
// Escribir usando una Response
await file.write(new Response("Hello World!"));
// Firmar previamente una URL
const url = file.presign({
expiresIn: 60 * 60 * 24, // 1 día
acl: "public-read",
});
// Eliminar el archivo
await file.delete();S3Client.prototype.write
Para subir o escribir un archivo a S3, llama a write en la instancia S3Client.
const client = new Bun.S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
endpoint: "https://s3.us-east-1.amazonaws.com",
bucket: "my-bucket",
});
await client.write("my-file.txt", "Hello World!");
await client.write("my-file.txt", new Response("Hello World!"));
// equivalente a
// await client.file("my-file.txt").write("Hello World!");S3Client.prototype.delete
Para eliminar un archivo de S3, llama a delete en la instancia S3Client.
const client = new Bun.S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
});
await client.delete("my-file.txt");
// equivalente a
// await client.file("my-file.txt").delete();S3Client.prototype.exists
Para verificar si existe un archivo en S3, llama a exists en la instancia S3Client.
const client = new Bun.S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
});
const exists = await client.exists("my-file.txt");
// equivalente a
// const exists = await client.file("my-file.txt").exists();S3File
Las instancias S3File se crean llamando al método de instancia S3Client o la función s3.file(). Como Bun.file(), las instancias S3File son lazy. No se refieren a algo que necesariamente existe en el momento de la creación. Por eso todos los métodos que no involucran solicitudes de red son completamente sincrónicos.
interface S3File extends Blob {
slice(start: number, end?: number): S3File;
exists(): Promise<boolean>;
unlink(): Promise<void>;
presign(options: S3Options): string;
text(): Promise<string>;
json(): Promise<any>;
bytes(): Promise<Uint8Array>;
arrayBuffer(): Promise<ArrayBuffer>;
stream(options: S3Options): ReadableStream;
write(
data: string | Uint8Array | ArrayBuffer | Blob | ReadableStream | Response | Request,
options?: BlobPropertyBag,
): Promise<number>;
exists(options?: S3Options): Promise<boolean>;
unlink(options?: S3Options): Promise<void>;
delete(options?: S3Options): Promise<void>;
presign(options?: S3Options): string;
stat(options?: S3Options): Promise<S3Stat>;
/**
* El tamaño no está disponible sincrónicamente porque requiere una solicitud de red.
*
* @deprecated Usa `stat()` en su lugar.
*/
size: NaN;
// ... más omitido por brevedad
}Como Bun.file(), S3File extiende Blob, por lo que todos los métodos disponibles en Blob también están disponibles en S3File. La misma API para leer datos de un archivo local también está disponible para leer datos de S3.
| Método | Salida |
|---|---|
await s3File.text() | string |
await s3File.bytes() | Uint8Array |
await s3File.json() | JSON |
await s3File.stream() | ReadableStream |
await s3File.arrayBuffer() | ArrayBuffer |
Eso significa que usar instancias S3File con fetch(), Response, y otras APIs web que aceptan instancias Blob simplemente funciona.
Lecturas parciales con slice
Para leer un rango parcial de un archivo, puedes usar el método slice.
const partial = s3file.slice(0, 1024);
// Leer el rango parcial como Uint8Array
const bytes = await partial.bytes();
// Leer el rango parcial como una cadena
const text = await partial.text();Internamente, esto funciona usando el encabezado HTTP Range para solicitar solo los bytes que quieres. Este método slice es el mismo que Blob.prototype.slice.
Eliminar archivos de S3
Para eliminar un archivo de S3, puedes usar el método delete.
await s3file.delete();
// await s3File.unlink();delete es lo mismo que unlink.
Códigos de error
Cuando la API S3 de Bun lanza un error, tendrá una propiedad code que coincide con uno de los siguientes valores:
ERR_S3_MISSING_CREDENTIALSERR_S3_INVALID_METHODERR_S3_INVALID_PATHERR_S3_INVALID_ENDPOINTERR_S3_INVALID_SIGNATUREERR_S3_INVALID_SESSION_TOKEN
Cuando el servicio de Almacenamiento de Objetos S3 devuelve un error (es decir, no Bun), será una instancia S3Error (una instancia Error con el nombre "S3Error").
Métodos estáticos de S3Client
La clase S3Client proporciona varios métodos estáticos para interactuar con S3.
S3Client.write (estático)
Para escribir datos directamente a una ruta en el bucket, puedes usar el método estático S3Client.write.
import { S3Client } from "bun";
const credentials = {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};
// Escribir cadena
await S3Client.write("my-file.txt", "Hello World");
// Escribir JSON con tipo
await S3Client.write("data.json", JSON.stringify({ hello: "world" }), {
...credentials,
type: "application/json",
});
// Escribir desde fetch
const res = await fetch("https://example.com/data");
await S3Client.write("data.bin", res, credentials);
// Escribir con ACL
await S3Client.write("public.html", html, {
...credentials,
acl: "public-read",
type: "text/html",
});Esto es equivalente a llamar a new S3Client(credentials).write("my-file.txt", "Hello World").
S3Client.presign (estático)
Para generar una URL firmada previamente para un archivo S3, puedes usar el método estático S3Client.presign.
import { S3Client } from "bun";
const credentials = {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};
const url = S3Client.presign("my-file.txt", {
...credentials,
expiresIn: 3600,
});Esto es equivalente a llamar a new S3Client(credentials).presign("my-file.txt", { expiresIn: 3600 }).
S3Client.list (estático)
Para listar algunos o todos (hasta 1,000) objetos en un bucket, puedes usar el método estático S3Client.list.
import { S3Client } from "bun";
const credentials = {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};
// Listar (hasta) 1000 objetos en el bucket
const allObjects = await S3Client.list(null, credentials);
// Listar (hasta) 500 objetos bajo el prefijo `uploads/`, con campo owner para cada objeto
const uploads = await S3Client.list({
prefix: 'uploads/',
maxKeys: 500,
fetchOwner: true,
}, credentials);
// Verificar si hay más resultados disponibles
if (uploads.isTruncated) {
// Listar el siguiente lote de objetos bajo el prefijo `uploads/`
const moreUploads = await S3Client.list({
prefix: 'uploads/',
maxKeys: 500,
startAfter: uploads.contents!.at(-1).key
fetchOwner: true,
}, credentials);
}Esto es equivalente a llamar a new S3Client(credentials).list().
S3Client.exists (estático)
Para verificar si existe un archivo S3, puedes usar el método estático S3Client.exists.
import { S3Client } from "bun";
const credentials = {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};
const exists = await S3Client.exists("my-file.txt", credentials);El mismo método también funciona en instancias S3File.
import { s3 } from "bun";
const s3file = s3.file("my-file.txt", {
// ...credentials,
});
const exists = await s3file.exists();S3Client.size (estático)
Para verificar rápidamente el tamaño de un archivo S3 sin descargarlo, puedes usar el método estático S3Client.size.
import { S3Client } from "bun";
const credentials = {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};
const bytes = await S3Client.size("my-file.txt", credentials);Esto es equivalente a llamar a new S3Client(credentials).size("my-file.txt").
S3Client.stat (estático)
Para obtener el tamaño, etag y otros metadatos de un archivo S3, puedes usar el método estático S3Client.stat.
import { S3Client } from "bun";
const credentials = {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
// endpoint: "https://<account-id>.r2.cloudflarestorage.com", // Cloudflare R2
};
const stat = await S3Client.stat("my-file.txt", credentials);{
etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
lastModified: 2025-01-07T00:19:10.000Z,
size: 1024,
type: "text/plain;charset=utf-8",
}S3Client.delete (estático)
Para eliminar un archivo S3, puedes usar el método estático S3Client.delete.
import { S3Client } from "bun";
const credentials = {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
bucket: "my-bucket",
// endpoint: "https://s3.us-east-1.amazonaws.com",
};
await S3Client.delete("my-file.txt", credentials);
// equivalente a
// await new S3Client(credentials).delete("my-file.txt");
// S3Client.unlink es un alias de S3Client.delete
await S3Client.unlink("my-file.txt", credentials);Protocolo s3://
Para facilitar el uso del mismo código para archivos locales y archivos S3, se admite el protocolo s3:// en fetch y Bun.file().
const response = await fetch("s3://my-bucket/my-file.txt");
const file = Bun.file("s3://my-bucket/my-file.txt");Adicionalmente, puedes pasar opciones s3 a las funciones fetch y Bun.file.
const response = await fetch("s3://my-bucket/my-file.txt", {
s3: {
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
endpoint: "https://s3.us-east-1.amazonaws.com",
},
headers: {
range: "bytes=0-1023",
},
});UTF-8, UTF-16 y BOM (marca de orden de bytes)
Como Response y Blob, S3File asume la codificación UTF-8 por defecto.
Al llamar a uno de los métodos text() o json() en un S3File:
- Cuando se detecta una marca de orden de bytes (BOM) UTF-16, se tratará como UTF-16. JavaScriptCore soporta nativamente UTF-16, por lo que omite el proceso de transcodificación UTF-8 (y elimina el BOM). Esto es mayormente bueno, pero significa que si tienes caracteres de pares sustitutos inválidos en tu cadena UTF-16, se pasarán a JavaScriptCore (igual que el código fuente).
- Cuando se detecta un BOM UTF-8, se elimina antes de que la cadena se pase a JavaScriptCore y los puntos de código UTF-8 inválidos se reemplazan con el carácter de reemplazo Unicode (
\uFFFD). - UTF-32 no es compatible.