Objetivos de aprendizaje
Después de leer este artículo podrás:
- Entender cómo la carga multiparte divide, envía y une un archivo
- Subir un archivo grande y reanudar una carga interrumpida con Python
- Evitar errores comunes: partes muy pequeñas, ETag perdido y partes huérfanas
En esta página
- ¿Cómo funciona una carga multiparte?
- ¿Cuáles son los límites de multiparte en S3?
- ¿Cómo subir un archivo grande en Python?
- ¿Cómo reanudar una carga interrumpida?
- ¿Cómo subir desde el navegador sin pasar por tu servidor?
- ¿Cuándo usar el protocolo tus?
- ¿Qué errores evitar?
- Cómo hacerlo en Square Cloud
- Preguntas frecuentes
Enviar un video de 4 GB en una sola petición HTTP funciona hasta que el Wi-Fi falla al 97%. La subida se cae y empiezas desde cero. Además, el servidor que recibe ese archivo tiene que mantener una conexión abierta mucho tiempo, con timeouts de proxy y límites de tamaño del cuerpo de la petición en el camino.
La carga multiparte lo resuelve de raíz: el archivo se convierte en varias partes menores, cada una con su propia petición. Los números de abajo son de Amazon S3, un object storage que marca el estándar que sigue casi todo storage compatible con S3; revisa los límites actuales en la documentación oficial.
¿Cómo funciona una carga multiparte?#
La documentación de S3 describe tres pasos:
- Iniciar (
CreateMultipartUpload). El storage devuelve unUploadId, que identifica esa carga en todas las llamadas siguientes. Los metadatos del objeto van en esta llamada. - Enviar las partes (
UploadPart). Cada parte tiene un número de 1 a 10.000. El storage responde con unETagpara cada una. Puedes enviarlas en cualquier orden y en paralelo. - Completar (
CompleteMultipartUpload). Envías la lista de números de parte con sus ETags. El storage une las partes en el orden de los números y crea el objeto.
Volver a enviar una parte con un número ya usado sobrescribe la anterior. Así se reenvía una parte que falló sin tocar las demás.
¿Cuáles son los límites de multiparte en S3?#
| Elemento | Límite en Amazon S3 |
|---|---|
| Tamaño máximo del objeto | 48,8 TiB |
| Número de partes | 1 a 10.000 |
| Tamaño de cada parte | 5 MiB a 5 GiB (la última puede ser menor) |
De ahí salen dos cuentas. Con 10.000 partes como máximo, un archivo de 100 GB necesita partes de al menos 10 MB. Y partes de 5 MiB limitan el archivo a unos 48,8 GiB (10.000 × 5 MiB). Calcula el tamaño de la parte como tamaño_del_archivo / 10.000, redondeado hacia arriba, y nunca menos de 5 MiB.
Otros storages compatibles con S3 pueden tener límites distintos. Revisa la documentación de tu proveedor antes de fijar el tamaño de la parte.
¿Cómo subir un archivo grande en Python?#
El camino más corto es upload_file de boto3, que decide solo cuándo usar multiparte y envía las partes en hilos. Instálalo con pip install boto3.
import os
import boto3
from boto3.s3.transfer import TransferConfig
s3 = boto3.client(
"s3",
endpoint_url=os.environ.get("S3_ENDPOINT"), # omítelo en AWS S3
aws_access_key_id=os.environ["S3_ACCESS_KEY"],
aws_secret_access_key=os.environ["S3_SECRET_KEY"],
)
config = TransferConfig(
multipart_threshold=64 * 1024 * 1024, # predeterminado: 8 MiB
multipart_chunksize=64 * 1024 * 1024, # predeterminado: 8 MiB por parte
max_concurrency=10, # predeterminado: 10 hilos
)
s3.upload_file("video.mp4", "mi-bucket", "videos/video.mp4", Config=config)Los valores predeterminados de TransferConfig son 8 MiB para el umbral y el tamaño de parte, y 10 hilos. Si la conexión es estable, partes mayores significan menos peticiones. Si es inestable, partes menores pierden menos en cada fallo.
Para verificar, ejecuta aws s3 ls s3://mi-bucket/videos/ --human-readable (con --endpoint-url en otro storage) y compara el tamaño con el de tu archivo local.
¿Cómo reanudar una carga interrumpida?#
upload_file no reanuda por sí solo: si el proceso muere, las partes ya enviadas quedan en el storage, pero el siguiente upload_file empieza una carga nueva. Para reanudar, usa las llamadas de bajo nivel y guarda el upload ID y el ETag de cada parte en un archivo local a medida que se aceptan las partes.
import json
import os
import boto3
s3 = boto3.client(
"s3",
endpoint_url=os.environ.get("S3_ENDPOINT"),
aws_access_key_id=os.environ["S3_ACCESS_KEY"],
aws_secret_access_key=os.environ["S3_SECRET_KEY"],
)
BUCKET, KEY, PATH = "mi-bucket", "videos/video.mp4", "video.mp4"
PART = 64 * 1024 * 1024 # 64 MiB: cubre archivos de hasta ~625 GiB en 10.000 partes
STATE = PATH + ".upload.json" # upload ID y ETags, guardados tras cada parte
if os.path.exists(STATE):
with open(STATE) as f:
state = json.load(f)
else:
upload_id = s3.create_multipart_upload(Bucket=BUCKET, Key=KEY)["UploadId"]
state = {"upload_id": upload_id, "parts": {}}
def save():
with open(STATE, "w") as f:
json.dump(state, f)
save()
done = state["parts"] # número de parte (texto en el JSON) -> ETag
with open(PATH, "rb") as f:
number = 1
while chunk := f.read(PART):
if str(number) not in done:
r = s3.upload_part(
Bucket=BUCKET, Key=KEY, UploadId=state["upload_id"],
PartNumber=number, Body=chunk,
)
done[str(number)] = r["ETag"]
save()
print("parte", number, "enviada")
number += 1
s3.complete_multipart_upload(
Bucket=BUCKET, Key=KEY, UploadId=state["upload_id"],
MultipartUpload={
"Parts": [
{"PartNumber": int(n), "ETag": e}
for n, e in sorted(done.items(), key=lambda item: int(item[0]))
]
},
)
os.remove(STATE)
print("completado")Si el script se cae a la mitad, ejecútalo de nuevo: lee el archivo de estado, se salta las partes ya registradas y envía solo las que faltan. La documentación de S3 pide completar con tu propia lista de números y ETags; list_parts sirve solo para comprobar qué llegó al storage. PART debe ser el mismo en ambas ejecuciones, o las partes antiguas no encajarán. El código lee una parte a la vez, así que la memoria usada es la de una parte, no la del archivo.
El ejemplo envía las partes en secuencia para que el código sea fácil de leer. Para paralelismo, usa el upload_file del ejemplo anterior o un ThreadPoolExecutor que llame a upload_part.
¿Cómo subir desde el navegador sin pasar por tu servidor?#
El patrón habitual es que tu servidor firme una URL prefirmada por parte y que el navegador envíe cada parte directo al storage. En el servidor, después de create_multipart_upload:
url = s3.generate_presigned_url(
"upload_part",
Params={"Bucket": BUCKET, "Key": KEY, "UploadId": upload_id, "PartNumber": 1},
ExpiresIn=3600,
)En el navegador, corta el archivo con file.slice(inicio, fin) y envía cada trozo con fetch(url, { method: "PUT", body: trozo }). La respuesta trae el ETag en la cabecera ETag. Para que JavaScript pueda leerla, la configuración de CORS del bucket debe exponer esa cabecera (ExposeHeaders). Al final, el navegador devuelve la lista de ETags a tu servidor, que llama a complete_multipart_upload.
¿Cuándo usar el protocolo tus?#
La carga multiparte de S3 se puede reanudar porque el storage guarda las partes. tus es otro camino: un protocolo abierto sobre HTTP (versión 1.0.0) en el que el servidor recuerda cuántos bytes recibió. El cliente pregunta con HEAD y continúa con PATCH:
HEAD /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Tus-Resumable: 1.0.0
HTTP/1.1 200 OK
Upload-Offset: 70
Upload-Length: 100
Cache-Control: no-store
Tus-Resumable: 1.0.0PATCH /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Tus-Resumable: 1.0.0
Upload-Offset: 70
Content-Type: application/offset+octet-stream
Content-Length: 30
[30 bytes restantes]Si el Upload-Offset enviado no coincide con el del servidor, este responde 409 Conflict. Elige tus cuando ya operas un servidor de cargas y quieres reanudar desde cualquier cliente. Para enviar directo al storage, la carga multiparte con URLs prefirmadas evita ese servidor.
¿Qué errores evitar?#
- Partes menores de 5 MiB. En S3, toda parte excepto la última debe tener al menos 5 MiB.
- Perder el ETag o el número de parte.
completeexige la lista completa. Guarda cada par en cuanto la parte sea aceptada. - Cambiar el tamaño de la parte al reanudar. Las partes antiguas dejan de encajar con las nuevas.
- Dejar cargas incompletas. En S3, las partes quedan guardadas y se cobran hasta que la carga se complete o se cancele. Llama a
abort_multipart_uploadal desistir y crea una regla de ciclo de vida conAbortIncompleteMultipartUpload(por ejemplo,DaysAfterInitiation: 7). Mira también reglas de ciclo de vida. - Esperar que el ETag sea el MD5 del archivo. En objetos multiparte, el ETag no es necesariamente el MD5 de los datos. Para comprobar la integridad, usa los checksums de S3.
- Usar multiparte en archivos pequeños. Cada parte es una petición más. Para archivos pequeños, un PUT simple lo resuelve con una sola petición.
Cómo hacerlo en Square Cloud#
En el Blob Storage de Square Cloud, el video de 4 GB del inicio cabe de sobra: cada objeto puede pesar hasta 10 GB. La carga multiparte está disponible y solo es obligatoria por encima de 100 MB por PUT; por debajo, un PUT simple basta. Como la API es compatible con S3, los dos scripts de este artículo funcionan sin cambiar la lógica: define S3_ENDPOINT=https://s3-blob.squarecloud.app, AWS_DEFAULT_REGION=auto y las claves S3 que generas en la pestaña "Acceso S3" del panel, y cambia mi-bucket por public o private, los buckets fijos de la cuenta en los que escribes.
El multipart_threshold de 64 MiB del ejemplo ya queda por debajo de los 100 MB, así que todo archivo que exige multiparte pasa por ella, y el script de reanudación usa las mismas llamadas de multiparte que acepta el Blob. Los límites de parte de la tabla son los de Amazon S3; en el Blob, lo que cambia es el tope de 10 GB del objeto final. Para archivos de hasta 100 MB enviados desde el navegador, basta una sola URL prefirmada de PUT: vale hasta 7 días, y el CORS del Blob acepta PUT desde cualquier origen.
No hay tarifa de entrada ni de salida, y cada plan incluye una cuota de 5 GB a 250 GB. Genera las claves en el panel y ejecuta el script de reanudación con un archivo tuyo, interrumpiéndolo a la mitad para ver cómo salta las partes ya enviadas. Conoce el Blob Storage y revisa la cuota de cada plan en Precios.
Blob Storage
Object storage compatible con S3, sin tarifas de salida
Preguntas frecuentes
¿Desde qué tamaño conviene usar carga multiparte?
La documentación de Amazon S3 recomienda considerar multiparte desde los 100 MB. boto3 ya usa multiparte por sí solo por encima de 8 MiB de forma predeterminada. En conexiones inestables conviene usarla antes.
¿Cuál es el tamaño máximo de cada parte en S3?
En Amazon S3 cada parte mide de 5 MiB a 5 GiB, salvo la última, que puede ser menor. Una carga admite hasta 10.000 partes y el objeto final puede llegar a 48,8 TiB. Los storages compatibles con S3 pueden tener límites propios.
¿Multiparte y carga reanudable son lo mismo?
No. Multiparte es la forma en que S3 divide un objeto en partes. Reanudable es poder continuar donde te quedaste, y multiparte lo permite porque las partes ya enviadas quedan guardadas. El protocolo tus llega al mismo resultado por otro camino, con HEAD y PATCH.
¿Qué pasa con las partes de una carga que nunca terminó?
En Amazon S3 las partes quedan guardadas y se cobran hasta que completes o canceles la carga. Configura una regla de ciclo de vida con AbortIncompleteMultipartUpload para limpiarlas.
Más en Storage
Ver los 15 artículos- Qué es una URL prefirmada (presigned URL)Una URL prefirmada (presigned URL) es un enlace temporal a un archivo privado que funciona sin login. Mira cómo funciona, sus límites y cuándo usarla.
- Qué es el almacenamiento de objetosEl almacenamiento de objetos guarda archivos como objetos con clave y metadatos, accesibles por una API HTTP. Mira cómo funciona, para qué sirve y sus límites.
- Qué es S3 y qué significa 'compatible con S3'S3 es el almacenamiento de objetos de Amazon y su API se volvió el estándar del sector. Aprende qué significa ser compatible con S3 y cómo conectarte.
- Archivos públicos o privados: cómo controlar quién accedeUn archivo público abre para quien tenga la URL; uno privado, solo con un enlace autorizado. Aprende a elegir, probar el acceso y compartir con seguridad.