Recibir y servir imágenes

Avanzado
Nest
Nest
Actualizado: 06/06/2025

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

La gestión de subida y visualización de archivos, especialmente imágenes, es una funcionalidad común en muchas aplicaciones web. En NestJS, podemos lograr esto de manera eficiente utilizando la integración con librerías como Multer y configurando el servidor para servir contenido estático.

¿Te está gustando esta lección?

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Instalación de tipos

Para manejar la subida de archivos, NestJS se apoya en Multer, una librería de middleware de Node.js para el manejo de multipart/form-data, que es el formato principal para subir archivos.

Ejecutar el comando:

npm i multer
npm i -D @types/multer

Configuración

La configuración principal para la subida de archivos se realiza en tu AppModule utilizando MulterModule. Aquí definimos dónde se guardarán los archivos y cómo se les asignará un nombre.

// src/app.module.ts
import { Module } from '@nestjs/common';
import { MulterModule } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { v4 as uuidv4 } from 'uuid'; // Para generar nombres únicos
import { extname, join } from 'path'; // Para obtener la extensión del archivo y uniones de rutas

@Module({
  imports: [
    // Configuración global de Multer
    MulterModule.register({
      storage: diskStorage({
        // Define la carpeta destino donde se guardarán los archivos.
        // Asegúrate de que esta carpeta exista en la raíz de tu backend.
        destination: join(__dirname, '..', 'uploads'), 
        
        // Define cómo se nombrarán los archivos antes de guardarlos.
        // Generamos un UUID para un nombre único y conservamos la extensión original.
        filename: (req, file, callback) => {
          const uniqueSuffix = uuidv4();
          const fileExtension = extname(file.originalname);
          const fileName = `${uniqueSuffix}${fileExtension}`;
          callback(null, fileName);
        },
      }),
      // Opcional pero recomendado: Límite de tamaño de archivo (ej. 5 MB)
      limits: {
        fileSize: 1024 * 1024 * 5, // 5 MB
      },
      // Opcional pero recomendado: Filtro para permitir solo ciertos tipos de archivos (ej. imágenes)
      fileFilter: (req, file, callback) => {
        // Validar tipos MIME específicos de imagen
        if (!file.mimetype.match(/(image\/jpeg|image\/png|image\/gif)$/)) {
          return callback(new Error('Solo se permiten archivos de imagen (JPEG, PNG, GIF).'), false);
        }
        callback(null, true);
      },
    }),
    // ... otros módulos
  ],
  controllers: [], // Agrega tus controladores aquí
  providers: [], // Agrega tus servicios aquí
})
export class AppModule {}

Crear la carpeta uploads en la raíz de tu proyecto de backend (al mismo nivel que src o node_modules), ya que es donde Multer guardará los archivos.

Recibir archivos en controlador

Para manejar la subida de archivos en un controlador, utilizamos el decorador @UseInterceptors() con FileInterceptor. Este interceptor se encarga de procesar el archivo en la solicitud HTTP y lo pone a disposición de tu método.

En un controlador dedicado (file.controller.ts) o un controlador de usuarios como por ejemplo user.controller.ts podemos crear un método para recibir el archivo:

// src/user/user.controller.ts (o un controlador dedicado para archivos)
import { 
  Controller, 
  Post, 
  UseInterceptors, 
  UploadedFile, 
  BadRequestException,
  Request, // Si necesitas acceder a request.user después de la autenticación
  UseGuards // Si estás usando guardias de autenticación
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
// import { AuthGuard } from '@nestjs/passport'; // Ejemplo si usas autenticación JWT

// Asume que tienes un UserRepository o un servicio de usuarios
// import { UserRepository } from './user.repository'; 

@Controller('users') // O 'files' si es un controlador dedicado
export class UsersController {
    // constructor(private readonly userRepository: UserRepository) {} // Inyecta tu repositorio/servicio de usuarios

    @Post('avatar')
    // 'file' es el nombre del campo en tu formulario (FormData) que contendrá el archivo
    @UseInterceptors(FileInterceptor('file')) 
    // @UseGuards(AuthGuard('jwt')) // Ejemplo: Protege la ruta con autenticación
    async uploadAvatar(
        @UploadedFile() file: Express.Multer.File, // El archivo subido
        @Request() req // Puedes acceder al objeto request si necesitas req.user de una guardia
    ) {
        // Validación básica (aunque el fileFilter de Multer ya hace mucho)
        if (!file) {
            throw new BadRequestException('No se ha proporcionado ningún archivo o el archivo es inválido.');
        }

        // Aquí, 'file' contiene información sobre el archivo guardado por Multer,
        // como su nombre (`file.filename`), ruta (`file.path`), etc.

        // Simulación: guardar la ruta del archivo en un atributo del usuario autenticado
        // req.user.photoUrl = file.filename;
        // await this.userRepository.save(req.user); // Guardar el usuario actualizado en la base de datos

        // Puedes devolver la información del archivo o una confirmación
        return { 
          message: 'Archivo subido con éxito', 
          filename: file.filename,
          // url: `http://localhost:3000/uploads/${file.filename}` // La URL completa del archivo
        };
    }
}
  • @UseInterceptors(FileInterceptor('file')): Este decorador le dice a NestJS que use el FileInterceptor para interceptar y procesar el archivo que viene en el cuerpo de la petición. 'file' es el nombre del campo form-data donde esperas el archivo.
  • @UploadedFile() file: Express.Multer.File: Este decorador inyecta el archivo procesado por Multer en el parámetro file de tu método. Express.Multer.File es la interfaz de tipo que describe el objeto del archivo. file.filename es el nombre único con el que se guardó el archivo en tu carpeta uploads.

Por último, se asocia la imagen a un usuario y se guarda el usuario en base de datos. 

Comprobar la tabla usuarios de la base de datos:

Servir el archivo guardado

Una vez que el archivo ha sido subido y guardado en tu backend, necesitas configurar NestJS para que pueda servir estos archivos a través de una URL HTTP. Esto se hace configurando una ruta para servir contenido estático.

Modifica tu archivo main.ts:

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as express from 'express'; // Necesitas importar express
import { join } from 'path'; // join para construir rutas de manera segura
import { ValidationPipe } from '@nestjs/common'; // Si ya lo tienes configurado, lo mantienes
import { MulterExceptionFilter } from './common/filters/multer-exception.filter'; // Si decides usar un filtro para Multer

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  app.enableCors(); // Habilita CORS si tu frontend está en un dominio diferente

  // Configuración global del ValidationPipe
  app.useGlobalPipes(new ValidationPipe({
    transform: true, // Habilita la transformación de datos para DTOs
    whitelist: true, // Elimina propiedades no definidas en los DTOs
    forbidNonWhitelisted: true, // Lanza un error si hay propiedades no definidas
  }));

  // Opcional pero recomendado: Añadir un filtro global para errores de Multer (si lo creas)
  // app.useGlobalFilters(new MulterExceptionFilter());

  // Configurar la carpeta 'uploads' para servir archivos estáticos.
  // Ejemplo de acceso: http://localhost:3000/uploads/nombre_del_archivo.png
  app.use('/uploads', express.static(join(__dirname, '..', 'uploads')));

  await app.listen(3000);
}
bootstrap();
  • app.use('/uploads', express.static(join(__dirname, '..', 'uploads'))): Esta línea le dice a Express (el framework subyacente de NestJS) que cualquier solicitud a /uploads debe buscar un archivo correspondiente dentro de la carpeta uploads de tu proyecto. join(__dirname, '..', 'uploads') construye una ruta absoluta y segura a tu carpeta uploads.

Consideraciones de Seguridad:

Servir archivos subidos directamente por los usuarios desde una carpeta pública puede ser un riesgo de seguridad si no se implementan validaciones robustas. Asegúrate de que tus filtros de fileFilter y limits en MulterModule sean estrictos. Para entornos de producción o alta seguridad, considera:

  • Almacenamiento en la nube: Usar servicios como AWS S3, Google Cloud Storage o Azure Blob Storage.
  • Servicio a través de un controlador: En lugar de express.static, crear una ruta GET en NestJS que lea y sirva el archivo de forma programática, permitiendo un control más granular (ej. verificación de permisos) antes de servir el archivo.

Mostrar archivos en frontend

Una vez que tu backend está sirviendo los archivos, puedes mostrarlos en tu aplicación frontend. Por ejemplo, en Angular, si tienes la URL del backend y el nombre del archivo guardado en un atributo photoUrl de tu usuario:

<img [src]="'http://localhost:3000/uploads/' + user.photoUrl" alt="Avatar del usuario">

En casos donde la imagen se usa como background se podría aplicar de la siguiente manera:

<div class="card card-cover h-100 overflow-hidden text-bg-dark rounded-4 shadow-lg"
        [style.background-image]="'url(http://localhost:3000/uploads/' + author.photoUrl + ')'">
        
        <!-- código de la card -->
</div>

A tener en cuenta, a partir de este momento las imágenes vienen de http://localhost:3000/uploads/ del backend, por tanto debe agregarse esa ruta como prefijo al nombre de las imágenes cuando se utilizan en el frontend.

Imágenes Estáticas del Frontend: Para imágenes que no provienen del backend (como logos, iconos o imágenes estáticas del diseño de la aplicación), las sigues guardando en la carpeta assets de tu proyecto frontend (ej. src/assets/img/logo.svg) y las referenciarás directamente desde ahí:

Y luego referenciarlo desde HTML de la siguiente manera:

<a class="navbar-brand" routerLink="/">
  <img style="max-width:80px;" src="/assets/img/logo.svg" alt="logo de la aplicación">
</a>

Verificar imágenes en frontend

Siempre verifica en el navegador (en la pestaña de red de las herramientas de desarrollador) que las imágenes se estén cargando correctamente desde la ruta http://localhost:3000/uploads/... de tu backend NestJS.

Aprendizajes de esta lección

  • Configuración MulterModule
  • Recibir archivos en controladores de NestJS
  • Guardar archivos en una carpeta del sistema de ficheros
  • Servir archivos al frontend

Completa Nest y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración