es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo cargar un archivo en FastAPI, luego enviarlo a Amazon S3 y finalmente procesarlo?

Tengo un punto final de FastAPI que recibe un archivo, lo carga en s3 y luego lo procesa. Todo funciona bien excepto el procesamiento, que falla con este mensaje:

lang-txt
  File "/usr/local/lib/python3.9/site-packages/starlette/datastructures.py", line 441, in read
    return self.file.read(size)
  File "/usr/local/lib/python3.9/tempfile.py", line 735, in read
    return self._file.read(*args)
ValueError: Operación de E/S en archivo cerrado.

Mi código simplificado se ve así:

async def process(file: UploadFile):
    reader = csv.reader(iterdecode(file.file.read(), "utf-8"), dialect="excel")  # ¡Esto falla!
    datarows = []
    for row in reader:
        datarows.append(row)
    return datarows

¿Cómo puedo leer el contenido del archivo cargado?

ACTUALIZACIÓN

Logré aislar un poco más el problema. Aquí está mi punto final simplificado:

import boto3
from loguru import logger
from botocore.exceptions import ClientError

UPLOAD = True

@router.post("/")
async def upload(file: UploadFile = File(...)):
    if UPLOAD:
        # Cargar el archivo
        s3_client = boto3.client("s3", endpoint_url="http://localstack:4566")
        try:
            s3_client.upload_fileobj(file.file, "local", "myfile.txt")
        except ClientError as e:
            logger.error(e)
    contents = await file.read()
    return JSONResponse({"message": "¡Éxito!"})

Si UPLOAD es True, obtengo el error. Si no lo es, todo funciona bien. Parece que boto3 está cerrando el archivo después de cargarlo. ¿Hay alguna forma de volver a abrir el archivo? ¿O enviar una copia a upload_fileobj?

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    El problema parece estar en file.file, el cual ya está abierto y se está utilizando para la subida. Para leer el contenido del archivo subido, puedes utilizar el atributo contents del objeto UploadFile en su lugar. Aquí tienes una versión actualizada de tu código:

    async def process(file: UploadFile):
        content = await file.read()  # leer el contenido del archivo desde el objeto UploadFile
        reader = csv.reader(content.decode().splitlines(), dialect="excel")
        datarows = []
        for row in reader:
            datarows.append(row)
        return datarows
    

    Con respecto al problema actualizado de boto3 cerrando el archivo, una solución es buscar el archivo al principio antes de leerlo de nuevo, como se muestra a continuación:

    async def upload(file: UploadFile = File(...)):
        if UPLOAD:
            # Subir el archivo
            s3_client = boto3.client("s3", endpoint_url="http://localstack:4566")
            try:
                file.file.seek(0)  # buscar el archivo al principio
                s3_client.upload_fileobj(file.file, "local", "myfile.txt")
            except ClientError as e:
                logger.error(e)
            finally:
                file.file.seek(0)  # buscar el archivo al principio nuevamente, en caso de que necesites leerlo más tarde
    
        contents = await file.read()
        return JSONResponse({"message": "¡Éxito!"})
    

Comments are closed.