Estoy cargando archivos zip como UploadFile a través de FastAPI y quiero guardarlos en el sistema de archivos usando aiofiles async como este:
async def upload(in_file: UploadFile = File(...)): filepath = /path/to/out_file.zip async with aiofiles.open(filepath, 'wb') as f: while buffer := await in_file.read(1024): await f.write(buffer) await f.close() El archivo se crea en filepath , sin embargo, tiene un tamaño de 0B y unzip out_file.zip se produce el siguiente error:
Archive: out_file.zip End-of-central-directory signature not found. Either this file is not a zipfile, or it constitutes one disk of a multi-part archive. In the latter case the central directory and zipfile comment will be found on the last disk(s) of this archive. unzip: cannot find zipfile directory in one of out_file.zip or out_file.zip.zip, and cannot find out_file.zip.ZIP, period. print(in_file.content_type) genera application/x-zip-compressed y
python3 -m mimetypes out_file.zip produce tipo: aplicación/codificación zip: Ninguno
Pasé demasiado tiempo en este inconveniente y probé varias alternativas de bloqueo como:
with open(filepath, "wb") as f: f.write(in_file.file.read()) f.close()todo lo cual resultó en el mismo escenario. Estoy tratando de lograr esto con los archivos .zip en este momento, pero eventualmente estoy buscando una solución universal para los archivos binarios para guardarlos a medida que vienen porque no estoy procesando ninguno de los archivos, solo necesitan ser almacenados.
Si alguien pudiera señalarme lo que me falta, sería de gran ayuda.
Editar: antes de intentar escribir el archivo en mi sistema de archivos, estoy agregando una Entrada con algunos metadatos a mi base de datos a través de Motor:
@router.post("/") async def upload(in_file: UploadFile = File(...)): file_content = await in_file.read() file_db = {"name": in_file.filename, "size": len(file_content)} file_db_json = jsonable_encoder(file_db) added_file_db = await add_file(file_db_json) filepath = /path/to/out_file.zip async with aiofiles.open(filepath, 'wb') as f: while buffer := await in_file.read(1024): await f.write(buffer) return ResponseModel(added_file_db, "upload successful") El retorno en upload() confirma que la carga fue exitosa, los metadatos se agregan a la base de datos, el archivo se crea en mi sistema de archivos pero se rompe como se describe anteriormente. No sé cómo esto podría interferir con la escritura del contenido del archivo en mi disco, pero tal vez me equivoque.
Úselo como se muestra a continuación (tomado de esta respuesta ):
import aiofiles @app.post("/upload") async def upload(file: UploadFile = File(...)): async with aiofiles.open(file.filename, 'wb') as f: while content := await file.read(1024): # async read chunk await f.write(content) return {"Uploaded File": file.filename}Si necesita que el archivo se guarde en un directorio específico, use lo siguiente:
import aiofiles import os @app.post("/upload") async def upload(file: UploadFile = File(...)): filename = os.path.join('path/to/', file.filename) async with aiofiles.open(filename, 'wb') as f: while content := await file.read(1024): # async read chunk await f.write(content) return {"Uploaded File": file.filename} La edición reciente en su pregunta muestra que ya ha leído el contenido del archivo en la línea file_content = await in_file.read() , y por lo tanto, intentar leer el contenido nuevamente usando await in_file.read(1024) da como resultado una lectura de cero bytes. Por lo tanto, agregue los metadatos a la base de datos después de leer y guardar el archivo (puede usar una variable para mantener la longitud total del archivo, por ejemplo, total_len += len(buffer) ), o simplemente escriba el file_content del archivo en el archivo local, como se muestra abajo.
async def upload(file: UploadFile = File(...)): filename = os.path.join('path/to/', file.filename) async with aiofiles.open(filename, 'wb') as f: await f.write(file_content) return {"Uploaded Filename": file.filename} En aras de la exhaustividad, también debo mencionar que hay un "cursor" interno (o "puntero de archivo") que indica la posición desde la cual se leerá (o escribirá) el contenido del archivo. Al llamar a read() lee todo el camino hasta el final del búfer, dejando cero bytes más allá del cursor. Por lo tanto, también se podría usar el método seek() para establecer la posición actual del cursor en 0 (es decir, rebobinar el cursor hasta el inicio del archivo). Segúnla documentación de FastAPI :
seek(offset): Va a la posición de byte deoffset (int)en el archivo.
- Por ejemplo,
await myfile.seek(0)iría al inicio del archivo.- Esto es especialmente útil si ejecuta
await myfile.read()una vez y luego necesita leer el contenido nuevamente.