Cómo generar URLs pre-firmadas para Cloudflare R2 con Astro en Cloudflare Workers
LaunchFast Logo LaunchFast

Cómo generar URLs pre-firmadas para Cloudflare R2 con Astro en Cloudflare Workers

Rishi Raj Jain
How to Generate Pre-signed URLs for Cloudflare R2 with Cloudflare Workers

En esta guía, aprenderás cómo generar URLs pre-firmadas para Cloudflare R2 con Astro en Cloudflare Workers. Pasarás por el proceso de configurar un nuevo proyecto Astro, habilitar la representación del lado del servidor usando el adaptador de Cloudflare, obtener credenciales de Cloudflare R2 y luego crear funciones para generar URLs pre-firmadas para la recuperación y carga desde Cloudflare R2.

Kits de inicio de alta calidad con flujo de autenticación integrada (Auth.js), carga de objetos (AWS, Clouflare R2, Firebase Storage, Supabase Storage), pagos integrados (Stripe, LemonSqueezy), flujo de verificación de correo electrónico (Resend, Postmark, Sendgrid) y mucho más . Compatible con cualquier base de datos (Redis, Postgres, MongoDB, SQLite, Firestore).

Requisitos previos

Para seguir, necesitarás:

Tabla de Contenidos

Crear una nueva aplicación Astro

Comencemos creando un nuevo proyecto Astro. Abre tu terminal y ejecuta el siguiente comando:

Terminal window
npm create astro@latest my-app

npm create astro es la forma recomendada para crear rápidamente un proyecto Astro.

Cuando se te pida, elige:

  • Usar plantilla mínima (vacía) cuando se te pregunte cómo comenzar el nuevo proyecto.
  • cuando se te pregunte si deseas instalar dependencias.
  • cuando se te pregunte si deseas inicializar un repositorio git.

Una vez hecho esto, puedes moverte al directorio del proyecto y comenzar la aplicación:

Terminal window
cd my-app
npm run dev

La aplicación debería estar ejecutándose en localhost:4321. A continuación, ejecuta el siguiente comando para instalar la biblioteca necesaria para construir la aplicación:

Terminal window
npm install aws4fetch

Se instala la siguiente biblioteca:

  • aws4fetch: Un cliente AWS para entornos que soportan fetch y SubtleCrypto.

Integrar el adaptador de Cloudflare en tu proyecto Astro

Para generar URLs pre-firmadas para cada objeto dinámicamente, habilitarás la representación del lado del servidor en tu proyecto Astro a través del adaptador de Cloudflare. Ejecuta el siguiente comando:

Terminal window
npx astro add cloudflare

Cuando se te pida, elige lo siguiente:

  • Y cuando se te pregunte si deseas instalar las dependencias de Cloudflare.
  • Y cuando se te pregunte si deseas realizar cambios en el archivo de configuración de Astro.

Has habilitado con éxito la representación del lado del servidor en Astro.

Para asegurarte de que la salida sea desplegable en Cloudflare Workers, crea un archivo wrangler.toml en la raíz del proyecto con el siguiente código:

wrangler.toml
name = "cloudflare-r2-astro-workers"
main = "dist/_worker.js"
compatibility_date = "2025-04-01"
compatibility_flags = [ "nodejs_compat" ]
[assets]
directory="dist"
binding="ASSETS"
[vars]
AWS_KEY_ID=""
AWS_S3_BUCKET_NAME=""
AWS_SECRET_ACCESS_KEY=""
CLOUDFLARE_R2_ACCOUNT_ID=""

Después de eso, asegúrate de tener tanto un archivo .env como un archivo wrangler.toml con las variables definidas para que puedan ser accedidas durante npm run dev y cuando se despliegue en Cloudflare Workers respectivamente.

Además, actualiza el archivo astro.config.mjs con lo siguiente para poder acceder a estas variables en el código de manera programática:

astro.config.mjs
// ... Existing imports...
import { defineConfig, envField } from 'astro/config'
export default defineConfig({
env: {
schema: {
AWS_KEY_ID: envField.string({ context: 'server', access: 'secret', optional: false }),
AWS_S3_BUCKET_NAME: envField.string({ context: 'server', access: 'secret', optional: false }),
AWS_SECRET_ACCESS_KEY: envField.string({ context: 'server', access: 'secret', optional: false }),
CLOUDFLARE_R2_ACCOUNT_ID: envField.string({ context: 'server', access: 'secret', optional: false }),
}
}
// adapter
})

Configurar Cloudflare R2

  • Navega a la página de Descripción General de Cloudflare R2.
Head to the Cloudflare R2 Overview
  • Crea un nuevo bucket en Cloudflare R2 y guarda el nombre como AWS_S3_BUCKET_NAME en las variables de entorno (.env & wrangler.toml)
Create a bucket in Cloudflare R2
  • Accede a los Detalles de la Cuenta R2 en Cloudflare R2.
Open R2 Account Details in Cloudflare R2
  • Recupera el ID de la Cuenta R2 de Cloudflare y guárdalo como CLOUDFLARE_R2_ACCOUNT_ID en las variables de entorno (.env & wrangler.toml)
Grab the Cloudflare R2 Account ID
  • Crea un token de API en Cloudflare R2.
Create an API token in Cloudflare R2
  • Obtén el ID de Clave de Acceso y la Clave de Acceso Secreta de Cloudflare R2 y guárdalos como AWS_KEY_ID y AWS_SECRET_ACCESS_KEY en las variables de entorno (.env & wrangler.toml) respectivamente.
Grab the Access Key ID and Secret Access Key in Cloudflare R2

Generar las URLs pre-firmadas

1. Acceder a las Variables de Entorno

El primer paso es acceder a las variables de entorno necesarias durante el tiempo de ejecución para crear un Cliente AWS a través de aws4fetch. Desde Astro 5.6 en adelante, la forma en que deseas acceder a las variables de entorno en tiempo de ejecución en tu código es usando la función getSecret de astro:env/server para mantener las cosas independientes del proveedor. Esto es crucial para almacenar información sensible de manera segura sin codificarla directamente en tu aplicación. Recuperarás las siguientes variables:

  • ID de Clave de Acceso de Cloudflare R2 (como AWS_KEY_ID)
  • Nombre del Bucket de Cloudflare (como AWS_S3_BUCKET_NAME)
  • Clave de Acceso Secreta de Cloudflare R2 (como AWS_SECRET_ACCESS_KEY)
  • ID de Cuenta de Cloudflare R2 (como CLOUDFLARE_R2_ACCOUNT_ID)
src/storage/r2.ts
import { getSecret } from 'astro:env/server'
const accessKeyId = getSecret('AWS_KEY_ID')
const s3BucketName = getSecret('AWS_S3_BUCKET_NAME')
const secretAccessKey = getSecret('AWS_SECRET_ACCESS_KEY')
const r2AccountId = getSecret('CLOUDFLARE_R2_ACCOUNT_ID')

2. Definir el Cliente AWS

A continuación, definirás la función defineAws4Fetch que crea una instancia de cliente AWS. Esta función verifica si las credenciales AWS requeridas están configuradas y devuelve una nueva instancia de AwsClient configurada para R2.

src/storage/r2.ts
import { AwsClient } from 'aws4fetch'
// ...Existing Code...
async function defineAws4Fetch(): Promise<AwsClient> {
if (!accessKeyId || !secretAccessKey) {
throw new Error(`AWS_KEY_ID OR AWS_SECRET_ACCESS_KEY environment variable(s) are not set.`)
}
return new AwsClient({
accessKeyId,
secretAccessKey,
service: 's3',
region: 'auto',
})
}

3. Determinar las URLs de R2

Necesitarás generar URLs únicas para cada carga de archivo a Cloudflare R2. La función getR2URL a continuación se encarga de construir la URL correcta basada en el nombre del archivo y el nombre del bucket.

src/storage/r2.ts
// ...Existing Code...
function getR2URL({ Key }: { Key: string }) {
if (!s3BucketName) {
throw new Error(`AWS_S3_BUCKET_NAME environment variable(s) are not set.`)
}
return new URL(`https://${r2AccountId}.r2.cloudflarestorage.com/${s3BucketName}/${Key}`)
}

4. URL pre-firmada para OBTENER un Objeto R2 (recuperar)

La función getR2ObjectURL a continuación recupera una URL pre-firmada de un objeto desde Cloudflare R2. Genera una solicitud firmada que te permite acceder al archivo de manera segura.

src/storage/r2.ts
// ...Existing Code...
export async function getR2ObjectURL(Key: string) {
try {
const endpointUrl = getR2URL({ Key })
endpointUrl.searchParams.set('X-Amz-Expires', '3600')
const client = await defineAws4Fetch()
const signedRequest = await client.sign(new Request(endpointUrl), { aws: { signQuery: true } })
return signedRequest.url
} catch (e: any) {
const tmp = e.message || e.toString()
console.log(tmp)
return
}
}

5. URL pre-firmada para PONER un Objeto R2 (cargar)

La función uploadR2ObjectURL a continuación es responsable de generar una URL pre-firmada para cargar un archivo a Cloudflare R2. Sigue una estructura similar a la función getR2ObjectURL, generando una URL firmada que te permite cargar archivos de manera segura.

export async function uploadR2ObjectURL(file: { name: string; type: string }) {
try {
const Key = file.name
const endpointUrl = getR2URL({ Key })
endpointUrl.searchParams.set('X-Amz-Expires', '3600')
const client = await defineAws4Fetch()
const signedRequest = await client.sign(new Request(endpointUrl, { method: 'PUT', headers: { 'Content-Type': file.type } }), { method: 'PUT', aws: { signQuery: true } })
return signedRequest.url
} catch (e: any) {
const tmp = e.message || e.toString()
console.log(tmp)
return
}
}

6. Crear un Endpoint de Servidor (una Ruta API) en Astro

src/pages/api/storage.ts
import type { APIContext } from 'astro'
import { getR2ObjectURL, uploadR2ObjectURL } from '../../storage/r2'
// Define an asynchronous function named GET that accepts a request object.
export async function GET({ request }: APIContext) {
// Extract the 'file' parameter from the request URL.
const url = new URL(request.url)
const file = url.searchParams.get('file')
// Check if the 'file' parameter exists in the URL.
if (file) {
try {
const filePublicURL = await getR2ObjectURL(file)
// Return a response with the image's public URL and a 200 status code.
return new Response(filePublicURL)
} catch (error: any) {
// If an error occurs, log the error message and return a response with a 500 status code.
const message = error.message || error.toString()
console.log(message)
return new Response(message, { status: 500 })
}
}
// If the 'file' parameter is not found in the URL, return a response with a 400 status code.
return new Response('Invalid Request.', { status: 400 })
}
export async function POST({ request }: APIContext) {
// Extract the 'file' parameter from the request URL.
const url = new URL(request.url)
const type = url.searchParams.get('type')
const name = url.searchParams.get('name')
if (!type || !name) return new Response('Invalid Request.', {status:400})
try {
// Generate an accessible URL for the uploaded file
// Use this url to perform a GET to this endpoint with file query param valued as below
const publicUploadUrl = await uploadR2ObjectURL({ type, name })
// Return a success response with a message
return new Response(publicUploadUrl)
} catch (error: any) {
// If there was an error during the upload process, return a 403 response with the error message
const message = error.message || error.toString()
console.log(message)
return new Response(message, { status: 500 })
}
}

Desplegar en Cloudflare Workers

Para hacer que tu aplicación sea desplegable en Cloudflare Workers, crea un archivo llamado .assetsignore en el directorio public con el siguiente contenido:

_routes.json
_worker.js

A continuación, necesitarás usar la CLI de Wrangler para desplegar tu aplicación en Cloudflare Workers. Ejecuta el siguiente comando para desplegar:

Terminal window
npm run build && npx wrangler@latest deploy

Referencias

Conclusión

En esta publicación de blog, aprendiste cómo integrar Cloudflare R2 con Astro y Cloudflare Workers para cargas y recuperaciones de archivos. Siguiendo los pasos de implementación, puedes cargar y recuperar archivos de Cloudflare R2 de manera segura, asegurando que tu aplicación web tenga una solución de almacenamiento robusta y flexible.

Si deseas explorar secciones específicas con más detalle, ampliar ciertos conceptos o cubrir temas adicionales relacionados, por favor házmelo saber, ¡y estaré encantado de ayudar!

Learn More Consulta de Cloud Firestore con Astro en Cloudflare Workers
Consulta de Cloud Firestore con Astro en Cloudflare Workers April 25, 2025
Query de Redis con Astro en Cloudflare Workers
Query de Redis con Astro en Cloudflare Workers April 25, 2025
Cómo generar URLs pre-firmadas para Firebase Storage con Astro en Cloudflare Workers
Cómo generar URLs pre-firmadas para Firebase Storage con Astro en Cloudflare Workers April 24, 2025