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 comoonChangeconst [file, setFile] = useState(null);: Usamos el hookuseStatepara crear una variable de estado llamadafiley una funciónsetFilepara actualizarla. Inicialmente, el valor defileesnull.const [imageUrl, setImageUrl] = useState(null);: De manera similar, creamos otra variable de estadoimageUrly 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 objetoFormDatay añade el archivo seleccionado bajo la clave "image". iii. Envía esteFormDataal 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 estadoimageUrlcon esa URL. b. Hay un input de tipo "file" (<input type="file">). Cuando un usuario selecciona un archivo, el estadofilese 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 siimageUrltiene algún valor. Si es así, muestra la imagen usando la etiqueta<img>. El atributosrcde<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.