Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

394
Views
¿Cómo puedo usar el trabajo open_download_stream de motor con StreamingResponse de FastAPI?

Estoy creando un punto final de FastAPI donde el usuario del cliente web puede esencialmente descargar archivos que están almacenados en MongoDB como fragmentos de GridFS. Sin embargo, StreamingResponse de FastAPI no toma el objeto AsyncIOMotorGridOut supuestamente similar a un archivo devuelto por el método open_download_stream de motor.

Ya tengo un punto final que puede tomar archivos en un formulario y hacer que se carguen en MongoDB. Esperaría que una función auxiliar de descarga similar fuera tan simple como esta:

 async def upload_file(db, file: UploadFile): """ Uploads file to MongoDB GridFS file system and returns ID to be stored with collection document """ fs = AsyncIOMotorGridFSBucket(db) file_id = await fs.upload_from_stream( file.filename, file.file, # chunk_size_bytes=255*1024*1024, #default 255kB metadata={"contentType": file.content_type}) return file_id

Mi primer intento es usar un ayudante como este:

 async def download_file(db, file_id): """Returns AsyncIOMotorGridOut (non-iterable file-like object)""" fs = AsyncIOMotorGridFSBucket(db) stream = await fs.open_download_stream(file_id) # return download_streamer(stream) return stream

Mi punto final de FastAPI se ve así:

 app.get("/file/{file_id}") async def get_file(file_id): file = await download_file(db, file_id) return StreamingResponse(file, media_type=file.content_type)

Cuando intento descargar un archivo con un file_id válido, aparece este error: TypeError: 'AsyncIOMotorGridOut' object is not an iterator

Mi segundo intento ha sido hacer un generador para iterar sobre fragmentos del archivo:

 async def download_streamer(file: AsyncIOMotorGridOut): """ Returns generator file-like object to be served by StreamingResponse https://fastapi.tiangolo.com/advanced/custom-response/#streamingresponse """ chunk_size = 255*1024*1024 for chunk in await file.readchunk(): print(f"chunk: {chunk}") yield chunk

Luego uso el return download_streamer(stream) comentado en mi ayudante download_file , pero por alguna razón, cada fragmento es solo un número entero de 255 .

¿Cuál es la mejor manera de sacar un archivo de MongoDB usando motor y transmitiéndolo como una respuesta web de FastAPI sin usar un archivo temporal? (No tengo acceso al disco duro y no quiero almacenar todo el archivo en la memoria; solo quiero transmitir archivos desde MongoDB a través de FastAPI directamente al cliente, fragmento por fragmento).

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

Mi solución es crear un generador que esté en la sintaxis de Python 3.6+ según esta respuesta SO . Dicho iterador funciona con la variante asíncrona de StreamingResponse de FastAPI y lee un fragmento de GridFS a la vez ( el valor predeterminado es 255 KB por documento de motor ) mediante el método readchunk() . Este tamaño de fragmento se establece cuando el archivo se almacena en MongoDB mediante upload_from_stream() . Una implementación opcional sería usar .read(n) para leer n bytes a la vez. Elegí usar readchunk() para que se obtenga exactamente 1 documento de base de datos a la vez durante la transmisión (cada archivo GridFS se divide en partes y se almacena una parte a la vez en la base de datos)

 async def chunk_generator(grid_out): while True: # chunk = await grid_out.read(1024) chunk = await grid_out.readchunk() if not chunk: break yield chunk async def download_file(db, file_id): """Returns iterator over AsyncIOMotorGridOut object""" fs = AsyncIOMotorGridFSBucket(db) grid_out = await fs.open_download_stream(file_id) return chunk_generator(grid_out)

Una mejora futura será que download_file() devuelva una tupla para incluir no solo el generador, sino también metadatos como ContentType .

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!