Subida de archivos con Next.js 13 y Cloudinary
En el ecosistema moderno de desarrollo web, manejar adecuadamente la subida de archivos es esencial para cualquier aplicación interactiva. Con la llegada de Next.js 13, uno de los Frameworks basados en React y Javascript más populares y versatiles del sector, gestionar archivos se ha vuelto aún más eficiente gracias a las mejoras de los route handlers
. Cloudinary, un líder en la gestión de medios en la nube, se integra perfectamente con Next.js, permitiendo una subida de archivos optimizada y sin problemas. En este tutorial, abordaremos cómo aprovechar al máximo estas tecnologías, utilizando formData
para la subida de archivos, y estableciendo una conexión fluida entre Next.js y Cloudinary desde la misma aplicación (Frontend y Backend).
Requerimientos
Para poder hacer este tutorial es necesario conocer:
- Bases de Nextjs, para esto puedes ir al curso de Nextjs
Creación de proyecto Nextjs
Primero crearemos un proyecto de Nextjs:
npx create-next-app nextjs-cloudinary
cd nextjs-cloudinary
npm run dev
Código Cliente
Luego añadiremos algo de Código Cliente, de React para poder seleccionar un archivo y enviarlo a una Ruta de Backend.
En el archivo src/page.jsx
añade lo siguiente:
"use client";
import { useState } from "react";
function HomePage() {
const [file, setFile] = useState(null);
const [imageUrl, setImageUrl] = useState(null);
return (
<div>
<form
onSubmit={async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("image", file);
const response = await fetch("/api/upload", {
method: "POST",
body: formData,
});
const data = await response.json();
console.log(data);
setImageUrl(data.url)
}}
>
<input
type="file"
onChange={(e) => {
setFile(e.target.files[0]);
}}
/>
<button>Enviar</button>
</form>
{
imageUrl && (
<img src={imageUrl} alt="" />
)
}
</div>
);
}
export default HomePage;
Este código representa un componente React llamado HomePage
que permite al usuario subir una imagen a un servidor y luego mostrarla. Las partes importantes a tener en cuenta en este codigo son las siguientes:
"use client";
: Esto definiendo codigo del lado cliente en Nextjs 13, esto es util para poder usar hooks de react, como useState, useEffect, y poder manejar eventos comoonChange
const [file, setFile] = useState(null);
: Usamos el hookuseState
para crear una variable de estado llamadafile
y una funciónsetFile
para actualizarla. Inicialmente, el valor defile
esnull
.const [imageUrl, setImageUrl] = useState(null);
: De manera similar, creamos otra variable de estadoimageUrl
y su función actualizadorasetImageUrl
.- Luego creamos un componente con un formulario (
<form>
) que, al enviarlo, realiza la siguiente secuencia de acciones: i. Evita el comportamiento predeterminado del formulario cone.preventDefault();
. ii. Crea un objetoFormData
y añade el archivo seleccionado bajo la clave "image". iii. Envía esteFormData
al endpoint "/api/upload" mediante una petición HTTP POST. iv. Cuando recibe una respuesta, la convierte a formato JSON. v. Extrae la URL de la imagen de la respuesta y establece el estadoimageUrl
con esa URL. b. Hay un input de tipo "file" (<input type="file">
). Cuando un usuario selecciona un archivo, el estadofile
se actualiza con ese archivo. c. A continuación, hay un botón (<button>
) que, al hacer clic, envía el formulario. d. Después del formulario, hay una condición que verifica siimageUrl
tiene algún valor. Si es así, muestra la imagen usando la etiqueta<img>
. El atributosrc
de<img>
se establece en el valor deimageUrl
.
Y Al final, exporto el componente HomePage
para que pueda ser utilizado en otras partes de la aplicación.
En resumen, este componente permite a los usuarios seleccionar un archivo de imagen, enviarlo a un servidor y luego mostrar la imagen en la página.
Route Handler para subir archivo
La ruta de Backend luciria así. Esta se encarga de guardar el archivo en la carpeta Public.
import { nextresponse } from "next/server";
import { writefile } from "fs/promises";
import { v2 as cloudinary } from "cloudinary";
import path from "path";
cloudinary.config({
cloud_name: "",
api_key: "",
api_secret: "",
});
export async function post(request) {
const data = await request.formdata();
const image = data.get("image");
if (!image) {
return nextresponse.json("no se ha subido ninguna imagen", { status: 400 });
}
const bytes = await image.arraybuffer();
const buffer = buffer.from(bytes);
const filepath = path.join(process.cwd(), "public", image.name);
await writefile(filepath, buffer)
const response = await cloudinary.uploader.upload(filepath);
console.log(response.secure_url)
return nextresponse.json({
message: "imagen subida",
url: response.secure_url,
});
}
Intalación de Cloudinary
Luego instalaremos la biblioteca de npm adecuada para Cloudinary, el cual es el mismo paquete que se usa en Nodejs.
npm i cloudinary
Luego añadiremos la siguiente ruta para subir el archivo
Subiendo Buffer
Para subir un buffer en Nexjs podemos usar el siguiente código:
cloudinary.uploader
.upload_stream({
resource_type: "image",
// public_id: "olympic_flag",
format: "png",
}, function (error, result) {
console.log(result);
return NextResponse.json({ success: true });
})
.end(buffer);
Este codigo lo que hace es aceptar el Buffer y enviarlo a los servidores de Cloudinary como un Stream. Sin embargo este formato espera usar los callbacks
, así que es mejor cambiarlo por su formato async/await a traves del objeto Promise
Conclusión
En fin la combinación de tecnologías modernas como React, junto con la potencia y versatilidad de Next.js, da muchas mas ventajas a los desarrolladores. La nueva incorporación de route handlers
en Next.js 13 facilita la forma en la que gestionamos las rutas y los datos que llegan a ella, facilitando la subida de archivos y permitiendo procesarlos en el backend. Ademas que al integrarlo con plataformas robustas como Cloudinary nos permite no solo subir archivos, sino también optimizarlos y entregarlos de manera eficiente.
En resumen, al combinar estas tecnologías, no solo mejoramos la experiencia de desarrollo, y la escalabilidad de nuestras aplicaciones, sino que también ofrecemos a los usuarios finales una experiencia más fluida y simple.