Ctrl K
ComunidadGuías…

app/s3.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import os
from io import BytesIO
from typing import Optional

import boto3
import botocore.exceptions
import requests

from app import config
from app.log import LOG

_s3_client = None


def _get_s3client():
    global _s3_client
    if _s3_client is None:
        args = {
            "aws_access_key_id": config.AWS_ACCESS_KEY_ID,
            "aws_secret_access_key": config.AWS_SECRET_ACCESS_KEY,
            "region_name": config.AWS_REGION,
        }
        if config.AWS_ENDPOINT_URL:
            args["endpoint_url"] = config.AWS_ENDPOINT_URL
        _s3_client = boto3.client("s3", **args)
    return _s3_client


def upload_from_bytesio(key: str, bs: BytesIO, content_type="application/octet-stream"):
    bs.seek(0)

    if config.LOCAL_FILE_UPLOAD:
        file_path = os.path.join(config.UPLOAD_DIR, key)
        file_dir = os.path.dirname(file_path)
        os.makedirs(file_dir, exist_ok=True)
        with open(file_path, "wb") as f:
            f.write(bs.read())

    else:
        _get_s3client().put_object(
            Bucket=config.BUCKET,
            Key=key,
            Body=bs,
            ContentType=content_type,
        )


def upload_email_from_bytesio(path: str, bs: BytesIO, filename):
    bs.seek(0)

    if config.LOCAL_FILE_UPLOAD:
        file_path = os.path.join(config.UPLOAD_DIR, path)
        file_dir = os.path.dirname(file_path)
        os.makedirs(file_dir, exist_ok=True)
        with open(file_path, "wb") as f:
            f.write(bs.read())

    else:
        _get_s3client().put_object(
            Bucket=config.BUCKET,
            Key=path,
            Body=bs,
            # Support saving a remote file using Http header
            # Also supports Safari. More info at
            # https://github.com/eligrey/FileSaver.js/wiki/Saving-a-remote-file#using-http-header
            ContentDisposition=f'attachment; filename="{filename}.eml";',
        )


def download_from_s3(path: str) -> Optional[bytes]:
    if config.LOCAL_FILE_UPLOAD:
        file_path = os.path.join(config.UPLOAD_DIR, path)
        with open(file_path, "rb") as f:
            return f.read()
    resp = _get_s3client().get_object(
        Bucket=config.BUCKET,
        Key=path,
    )
    if not resp or "Body" not in resp:
        return None
    return resp["Body"].read()


def upload_from_url(url: str, upload_path):
    r = requests.get(url)
    upload_from_bytesio(upload_path, BytesIO(r.content))


def exists(key: str) -> bool:
    if config.LOCAL_FILE_UPLOAD:
        file_path = os.path.join(config.UPLOAD_DIR, key)
        return os.path.exists(file_path)

    try:
        _get_s3client().head_object(Bucket=config.BUCKET, Key=key)
        return True
    except botocore.exceptions.ClientError as e:
        error_code = str(e.response.get("Error", {}).get("Code", ""))
        status_code = str(
            e.response.get("ResponseMetadata", {}).get("HTTPStatusCode", "")
        )
        if error_code in ("NoSuchKey", "NotFound", "404") or status_code == "404":
            return False
        raise


def upload_raw(
    key: str,
    bs: BytesIO,
    content_type: Optional[str] = None,
    content_disposition: Optional[str] = None,
):
    bs.seek(0)

    if config.LOCAL_FILE_UPLOAD:
        file_path = os.path.join(config.UPLOAD_DIR, key)
        file_dir = os.path.dirname(file_path)
        os.makedirs(file_dir, exist_ok=True)
        with open(file_path, "wb") as f:
            f.write(bs.read())
    else:
        args = {
            "Bucket": config.BUCKET,
            "Key": key,
            "Body": bs,
        }
        if content_type is not None:
            args["ContentType"] = content_type
        if content_disposition is not None:
            args["ContentDisposition"] = content_disposition
        _get_s3client().put_object(**args)


def get_url(key: str, expires_in=3600) -> str:
    if config.LOCAL_FILE_UPLOAD:
        return config.URL + "/static/upload/" + key
    else:
        return _get_s3client().generate_presigned_url(
            ExpiresIn=expires_in,
            ClientMethod="get_object",
            Params={"Bucket": config.BUCKET, "Key": key},
        )


def delete(path: str):
    if config.LOCAL_FILE_UPLOAD:
        file_path = os.path.join(config.UPLOAD_DIR, path)
        os.remove(file_path)
    else:
        _get_s3client().delete_object(Bucket=config.BUCKET, Key=path)


def create_bucket_if_not_exists():
    s3client = _get_s3client()
    buckets = s3client.list_buckets()
    for bucket in buckets["Buckets"]:
        if bucket["Name"] == config.BUCKET:
            LOG.i("Bucket already exists")
            return
    s3client.create_bucket(Bucket=config.BUCKET)
    LOG.i(f"Bucket {config.BUCKET} created")