Saltar al contenido

Descargar un archivo desde un s3 Bucket a la computadora USUARIOS

Te recomendamos que revises esta resolución en un entorno controlado antes de pasarlo a producción, saludos.

Solución:

No debería necesitar guardar el archivo en el servidor. Simplemente puede descargar el archivo en la memoria y luego crear un Response objeto que contiene el archivo.

from flask import Flask, Response
from boto3 import client

app = Flask(__name__)


def get_client():
    return client(
        's3',
        'us-east-1',
        aws_access_key_id='id',
        aws_secret_access_key='key'
    )


@app.route('/blah', methods=['GET'])
def index():
    s3 = get_client()
    file = s3.get_object(Bucket='blah-test1', Key='blah.txt')
    return Response(
        file['Body'].read(),
        mimetype='text/plain',
        headers="Content-Disposition": "attachment;filename=test.txt"
    )


app.run(debug=True, port=8800)

Esto está bien para archivos pequeños, no habrá ningún tiempo de espera significativo para el usuario. Sin embargo, con archivos más grandes, esto afectará la UX. El archivo deberá descargarse completamente en el servidor y luego descargarse al usuario. Entonces, para solucionar este problema, use el Range argumento de palabra clave del get_object método:

from flask import Flask, Response
from boto3 import client

app = Flask(__name__)


def get_client():
    return client(
        's3',
        'us-east-1',
        aws_access_key_id='id',
        aws_secret_access_key='key'
    )


def get_total_bytes(s3):
    result = s3.list_objects(Bucket='blah-test1')
    for item in result['Contents']:
        if item['Key'] == 'blah.txt':
            return item['Size']


def get_object(s3, total_bytes):
    if total_bytes > 1000000:
        return get_object_range(s3, total_bytes)
    return s3.get_object(Bucket='blah-test1', Key='blah.txt')['Body'].read()


def get_object_range(s3, total_bytes):
    offset = 0
    while total_bytes > 0:
        end = offset + 999999 if total_bytes > 1000000 else ""
        total_bytes -= 1000000
        byte_range = 'bytes=offset-end'.format(offset=offset, end=end)
        offset = end + 1 if not isinstance(end, str) else None
        yield s3.get_object(Bucket='blah-test1', Key='blah.txt', Range=byte_range)['Body'].read()


@app.route('/blah', methods=['GET'])
def index():
    s3 = get_client()
    total_bytes = get_total_bytes(s3)

    return Response(
        get_object(s3, total_bytes),
        mimetype='text/plain',
        headers="Content-Disposition": "attachment;filename=test.txt"
    )


app.run(debug=True, port=8800)

Esto descargará el archivo en fragmentos de 1 MB y los enviará al usuario a medida que se descargan. Ambos han sido probados con un 40MB .txt expediente.

Una mejor manera de resolver este problema es crear una URL prefirmada. Esto le da una URL temporal que es válida hasta un cierto período de tiempo. También elimina su servidor de matraz como un proxy entre el depósito de AWS s3, lo que reduce el tiempo de descarga para el usuario.

def get_attachment_url():
   bucket = 'BUCKET_NAME'
   key = 'FILE_KEY'

   client: boto3.s3 = boto3.client(
     's3',
     aws_access_key_id=YOUR_AWS_ACCESS_KEY,
     aws_secret_access_key=YOUR_AWS_SECRET_KEY
   )

   return client.generate_presigned_url('get_object',
                                     Params='Bucket': bucket, 'Key': key,
                                     ExpiresIn=60) `

Comentarios y puntuaciones del artículo

Nos encantaría que puedieras mostrar este artículo si te fue de ayuda.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *