# Almacenamiento de ficheros (compatible con S3)

> Guarda fotos, documentos y subidas de tus usuarios en almacenes compatibles con S3, cada uno con su propia clave, y conéctalos a tus apps sin ver las claves.

Un **almacén de ficheros** (en inglés, *bucket*) es donde tu app guarda lo que no va en la base de datos: fotos de productos, PDF, avatares, lo que suben tus usuarios. Los almacenes de Dilohost son **compatibles con S3**, así que funcionan con las librerías de siempre (el disco `s3` de Laravel, el SDK de AWS, boto3…).

Cada almacén tiene **su propia clave**, que solo da acceso a ese almacén. Las claves no se muestran en la conversación: se le dan directamente a la app.

## Dos herramientas: una lee, otra cambia

| Herramienta | Acciones | Para qué |
|---|---|---|
| `storage_read` (solo lectura) | `list`, `download_url` | Ver los almacenes de un proyecto y descargar un fichero. |
| `storage` | `create_bucket`, `connection`, `upload_url`, `delete` | Crear, conectar a una app, subir y borrar. |

Están separadas para que tu agente (y tú) sepáis siempre si una acción solo mira o puede cambiar algo.

## Antes de empezar

El plan Gratis no incluye almacenes. Cuántos almacenes y cuánto espacio trae cada plan: [Planes y precios](/docs/cuenta/planes). El espacio del plan se reparte entre los almacenes de la cuenta.

## Crear un almacén

<p class="prompt">Crea un almacén de ficheros llamado fotos en el proyecto tienda.</p>

`storage.create_bucket` lo crea y te dice su capacidad. El nombre son de 1 a 20 letras minúsculas y números (por ejemplo `fotos`); si no dices ninguno, se llama `files`.

## Conectarlo a una app

<p class="prompt">Conecta el almacén fotos a la app web.</p>

`storage.connection` con `inject_into` guarda la conexión como **variables secretas** de la app y la aplica reiniciándola:

| Variable | Valor |
|---|---|
| `AWS_ACCESS_KEY_ID` | Clave de acceso del almacén |
| `AWS_SECRET_ACCESS_KEY` | Secreto de esa clave |
| `AWS_DEFAULT_REGION` | `us-east-1` |
| `AWS_BUCKET` | Nombre interno del almacén |
| `AWS_ENDPOINT` | Dirección interna, la que usa la app para leer y escribir |
| `AWS_URL` | Dirección pública del almacén (`https://s3.dilosite.es/<almacén>`) |
| `AWS_USE_PATH_STYLE_ENDPOINT` | `true` |

Sin `inject_into`, el agente solo ve el nombre del almacén, las direcciones y la región, nunca las claves.

### Laravel

Laravel lee estas variables tal cual en el disco `s3` de `config/filesystems.php`. Solo tienes que usarlo:

```php
use Illuminate\Support\Facades\Storage;

$ruta = $request->file('foto')->store('productos', 's3');
$contenido = Storage::disk('s3')->get($ruta);
```

Si es una app nueva, instala antes el adaptador: `composer require league/flysystem-aws-s3-v3`.

### Node.js

```js
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({
  region: process.env.AWS_DEFAULT_REGION,
  endpoint: process.env.AWS_ENDPOINT,
  forcePathStyle: true,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  },
});

await s3.send(new PutObjectCommand({ Bucket: process.env.AWS_BUCKET, Key: 'productos/pan.jpg', Body: fichero }));
```

### Python

```python
import os, boto3

s3 = boto3.client('s3',
    endpoint_url=os.environ['AWS_ENDPOINT'],
    region_name=os.environ['AWS_DEFAULT_REGION'],
    aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'])
s3.upload_file('pan.jpg', os.environ['AWS_BUCKET'], 'productos/pan.jpg')
```

## Subir o descargar un fichero suelto

A veces quieres meter o sacar un fichero sin pasar por tu app (un logo, una copia de un PDF):

<p class="prompt">Dame un enlace para subir logo.png al almacén fotos, en la carpeta marca.</p>

- `storage.upload_url` da un enlace temporal para **subir** un fichero con una petición `PUT` (por ejemplo, `curl -T logo.png "<enlace>"`).
- `storage_read.download_url` da un enlace temporal para **descargarlo**.

Los dos enlaces valen **1 hora**. La ruta del fichero (`object`) es relativa dentro del almacén, como `marca/logo.png`; no se admiten rutas absolutas ni `..`.

## Borrar un almacén

`storage.delete` **pide confirmación** y borra el almacén **con todos sus ficheros**. Es irreversible: los almacenes no tienen copia de seguridad propia.

## Relacionado

- [Variables de entorno y secretos](/docs/publicar/variables)
- [Bases de datos](/docs/datos/bases-de-datos)
- Referencia: [`storage`](/docs/referencia/herramientas#storage) y [`storage_read`](/docs/referencia/herramientas#storage-read)
