Descargar Archivos en Laravel

Video thumbnail

¿Cómo descargar un archivo en Laravel?, es una de esas tareas que parecen sencillas una vez que ya sabemos como realizar la carga de archivos en Laravel… y lo son, siempre que sepas desde dónde quieres hacer la descarga.

En mi caso, cuando empecé a trabajar con proyectos que servían libros digitales, descubrí que no era lo mismo ofrecer un archivo público que uno protegido.

En esta guía te explico ambas opciones —y algunas más avanzadas— con ejemplos reales y consejos prácticos.

En ambos casos empleamos el método download(), que indica la ruta del archivo que se va a descargar.

1 Introducción: las distintas formas de descargar archivos en Laravel

Laravel nos da varias formas de servir archivos al usuario:

Desde la carpeta public, para archivos accesibles directamente por el navegador.

Desde un disco (Storage), cuando necesitas control de acceso o privacidad.

Por streaming o desde servicios externos como Amazon S3.

Lo importante es entender la lógica: el método download() genera una respuesta HTTP que fuerza la descarga en el navegador, sin exponer la ruta física real del archivo.

2 Descarga desde carpeta public

Si el archivo está en public, simplemente puedes devolver la ruta hacia dicho archivo y dejar que el navegador se encargue de todo.

Este caso es el más simple, ya que la carpeta public es la única que se encuentra accesible directamente desde el navegador:

 public function download($file_name) {
    $file_path = public_path('files/'.$file_name);
    return response()->download($file_path);
  }

Laravel convierte esa ruta en una respuesta lista para descargar.
Es el método que suelo usar cuando se trata de recursos abiertos, como guías o plantillas gratuitas.

Ventajas:

  • Simple y directo.
  • No requiere configuración adicional.

Limitaciones:

  • No es seguro para archivos sensibles.
  • No puedes controlar quién accede al recurso.

3 Descarga desde un disco protegido

En este ejemplo empleo un disco, porque se trata de un recurso protegido.

Recordemos que todas las carpetas fuera de public son privadas, lo que las hace ideales para almacenar archivos a los que no queremos dar acceso directo, sino acceso controlado.

Aquí, fíjate: el disco es local, aunque también podrías usar Amazon u otros proveedores:

config\filesystems.php

'files_disk' => [
    'driver' => 'local',
    'root' => app()->storagePath()
],

La ruta, nuevamente, es local, y para organizarlos utilizo un path root, donde guardo todos los libros que, en base a alguna lógica (por ejemplo, cuando los compras), son los que permito descargar.

El proceso es sencillo:

Storage::disk('files_disk')->download($routefile);
Storage::disk('files_disk')->download('book/' . $file->file, $name . "." . $file->type);

En mi caso, este enfoque me resulta mucho más seguro.
Todo lo que está fuera de public es inaccesible desde el navegador, lo que garantiza que solo los usuarios autorizados puedan acceder.

Tip personal: suelo mantener una estructura clara de carpetas (books/, images/, invoices/) para evitar confusiones y mejorar la trazabilidad.

4. Descargas avanzadas: streaming y archivos remotos

Si trabajas con archivos grandes o quieres descargar desde proveedores externos, Laravel ofrece otras herramientas potentes.

Streaming con streamDownload()

Permite enviar el archivo por partes, ideal para archivos pesados:

return response()->streamDownload(function () use ($path) {
   echo Storage::disk('files_disk')->get($path);
}, 'archivo.pdf');

Descargar desde Amazon S3 o Google Cloud

Solo necesitas configurar el disco en filesystems.php y usarlo igual que el local:

return Storage::disk('s3')->download('docs/manual.pdf');

También puedes forzar un nombre de descarga diferente o un tipo MIME específico con headers:

return response()->download($path, 'mi-archivo.pdf', [
   'Content-Type' => 'application/pdf',
]);

En mi experiencia, este enfoque es excelente cuando se gestionan catálogos de archivos dinámicos o copias de respaldo.

5. Cómo permitir descargas solo a usuarios autenticados

La seguridad es clave.

Puedes controlar quién descarga un archivo simplemente protegiendo la ruta con middleware:

Route::get('/download/{file}', [FileController::class, 'download'])
   ->middleware('auth');

O bien, validar dentro del controlador:

if (auth()->user()->hasPurchased($file->id)) {
   return Storage::disk('files_disk')->download($file->path);
}
abort(403, 'No autorizado');

Así garantizas que cada descarga tiene lógica de negocio detrás.

En mi caso, esto fue esencial cuando implementé descargas de productos digitales con control de licencias.

6. Errores comunes al descargar archivos en Laravel (y cómo evitarlos)


Error    Causa    Solución
Archivo no encontrado    Ruta mal construida o disco incorrecto    Verifica storage_path() o disk()
Descarga vacía o dañada    Encoding incorrecto o salida previa al response    Asegúrate de no tener echo o dd() antes de return
Permiso denegado    Carpeta sin permisos de lectura    Corrige permisos con chmod o chown
Nombre incorrecto    No se especificó segundo parámetro en download()    Agrega $name y extensión manualmente

Consejo: siempre usa rutas absolutas (public_path(), storage_path()) para evitar errores en entornos distintos.

Extra: NO uses la carpeta public para el upload de ciertos archivos, protege tus archivos en la storage en Laravel

La descarga de archivos es una funcionalidad muy común en el desarrollo de software. Permitir que los usuarios descarguen archivos en base a reglas de negocio específicas de tu aplicación es habitual, por ejemplo, cuando vendes archivos alojados en la aplicación y, una vez adquiridos, los usuarios pueden descargarlos.

Para esto, la aplicación primero verifica el pago y, posteriormente, permite la descarga.

Archivos protegidos: no uses la carpeta public

Es importante notar que los archivos no deben estar alojados en la carpeta public, como sucede con imágenes cargadas mediante procesos de upload. Cualquier persona que conozca el nombre del archivo podría acceder a él sin restricciones.

En Laravel, la única carpeta accesible públicamente es public. Por lo tanto, para archivos con acceso controlado, no es recomendable usar esta carpeta.

Podemos almacenar archivos en cualquier otra carpeta de la aplicación, lo que resulta útil para escenarios en los que queremos controlar el acceso, como en la carpeta storage.

Configuración del disco de almacenamiento

Creamos un disco específico para almacenar los archivos protegidos:

// config/filesystems.php
'files_sell_uploads' => [
 'driver' => 'local',
 'root' => app()->storagePath(),
],

Subida de archivos

La función para subir un archivo podría verse así:

function uploadBook()
{
  $this->rules = [
      'fileBook' => 'nullable|mimes:epub,pdf|max:20024'
  ];
  $this->validate();
  if ($this->fileBook) {
      $name = time() . '.' . $this->fileBook->getClientOriginalExtension();
      $this->fileBook->storeAs('book', $name, 'files_sell_uploads');
      YourModel::create([
          'file' => $name,
          'type' => $this->fileBook->getClientOriginalExtension(),
          // otros campos según tu modelo
      ]);
  }
}

Control de acceso para la descarga

Para permitir la descarga solo a usuarios que cumplan ciertas condiciones, puedes usar algo como:

if ($filePayment && $file) {
   return Storage::disk('files_sell_uploads')->download('book/' . $file->file, "book." . $file->type);
}

Es importante destacar que la única forma de acceder a estos archivos vía HTTP es mediante la función anterior. Al estar almacenados fuera de public, los archivos no son accesibles directamente.

Función completa de descarga segura

public function downloadFile(File $file)
{
  $user = auth()->user() ?? auth('sanctum')->user();
  $filePayment = FilePayment::where(**condición**)->first();
  $file = File::where(**condición**)->first();
  if ($filePayment && $file) {
      return Storage::disk('files_sell_uploads')->download('book/' . $file->file, "book." . $file->type);
  }
  return response()->errorResponse("", 403, 'Producto no adquirido o no existe');
}

Con esta implementación, los archivos solo se descargan si se cumplen las condiciones de negocio (por ejemplo, el usuario compró el archivo).

Beneficios de este esquema

Este enfoque es ideal si quieres desarrollar una tienda en línea dentro de tu aplicación Laravel, donde los archivos a vender se almacenan de manera segura y el acceso se controla completamente desde tu código.

7. Conclusión y consejos finales

Descargar archivos en Laravel es tan simple o tan robusto como necesites.

Para archivos públicos, response()->download() basta.

Si manejas datos sensibles o necesitas control, usa discos en Storage y protege las rutas.

Y si trabajas con archivos grandes o servicios externos, streamDownload() y S3 son tus aliados.

En mi experiencia, la clave está en entender el contexto de seguridad y optimización, más que en la función en sí.

Laravel te da todas las herramientas; la diferencia está en cómo las combinas.

Preguntas frecuentes

¿Cuál es la diferencia entre public_path() y storage_path()?
public_path() apunta a la carpeta accesible por el navegador; storage_path() es privada y solo accesible mediante código.

¿Puedo descargar varios archivos a la vez?
Sí, puedes usar una librería como ZipArchive para generar un .zip y devolverlo con response()->download().

¿Se puede forzar el nombre del archivo?
Sí, usando el segundo parámetro del método download(), por ejemplo:
response()->download($path, 'mi-archivo.pdf').

El siguiente paso es optimizar tus consultas, conociendo como puedes usar la Cache en Laravel.

Acepto recibir anuncios de interes sobre este Blog.

Aprende cómo descargar archivos en Laravel paso a paso: desde rutas públicas hasta discos protegidos, almacenamiento en S3 y descargas por streaming. Incluye ejemplos reales, buenas prácticas de seguridad y código funcional en PHP.

| 👤 Andrés Cruz

🇺🇸 In english