import boto3 import app.config as config import uuid from fastapi import APIRouter, UploadFile, HTTPException from starlette import status from starlette.responses import StreamingResponse from botocore.exceptions import ClientError from app.s3 import schema_s3 s3_client = boto3.client( "s3", aws_access_key_id=config.S3_ACCESS_KEY_ID, aws_secret_access_key=config.S3_SECRET_ACCESS_KEY, endpoint_url=config.S3_ENDPOINT_URL, region_name=config.S3_REGION_NAME, ) router = APIRouter( prefix="/api/s3", tags=["S3 Manipulação Arquivos"], responses={404: {"description": "Not found"}}, ) @router.post("/upload", status_code=status.HTTP_200_OK) async def upload_to_s3(file: UploadFile): """ Faz upload de uma imagem para o bucket restrito. """ try: unique_filename = f"{uuid.uuid4()}-{file.filename}" s3_client.upload_fileobj( file.file, config.S3_BUCKET_NAME, unique_filename, ExtraArgs={ "ContentType": file.content_type, "Metadata": {"original_filename": file.filename} }, ) return {"message": "Arquivo salvo com sucesso", "nome do arquivo": unique_filename} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/images/url/") async def get_presigned_url(request: schema_s3.FileNameRequest): """ Gera uma URL pré-assinada para download do arquivo, com o nome original configurado. """ try: file_name = request.file_name # Nome do arquivo com UUID # Obter os metadados do arquivo para o nome original response = s3_client.head_object(Bucket=config.S3_BUCKET_NAME, Key=file_name) metadata = response.get("Metadata", {}) original_filename = metadata.get("original_filename", file_name) # Gerar uma URL pré-assinada para download presigned_url = s3_client.generate_presigned_url( "get_object", Params={ "Bucket": config.S3_BUCKET_NAME, "Key": file_name, "ResponseContentDisposition": f'attachment; filename="{original_filename}"' }, ExpiresIn=3600, # URL válida por 1 hora ) return {"url": presigned_url} except ClientError as e: if e.response["Error"]["Code"] == "NoSuchKey": raise HTTPException(status_code=404, detail="Arquivo não encontrado") raise HTTPException(status_code=500, detail="Erro ao gerar URL") @router.post("/images/url/simple/") async def generate_presigned_url(request: schema_s3.FileNameRequest): """ Gera uma URL pré-assinada para acessar o arquivo no MinIO (sem download automático). """ try: file_name = request.file_name # Nome do arquivo com UUID # Gerar uma URL pré-assinada sem configurar o download automático presigned_url = s3_client.generate_presigned_url( "get_object", Params={ "Bucket": config.S3_BUCKET_NAME, "Key": file_name }, ExpiresIn=3600, # URL válida por 1 hora ) return {"url": presigned_url} except ClientError as e: if e.response["Error"]["Code"] == "NoSuchKey": raise HTTPException(status_code=404, detail="File not found") raise HTTPException(status_code=500, detail="Error generating presigned URL") @router.post("/images/") async def get_image(request: schema_s3.FileNameRequest): """ Retorna uma imagem específica para download. O usuário precisa estar autenticado para acessar. """ try: file_name = request.file_name # Baixar o objeto do MinIO como um fluxo response = s3_client.get_object(Bucket=config.S3_BUCKET_NAME, Key=file_name) metadata = response.get("Metadata", {}) original_filename = metadata.get("original_filename", file_name) # Nome original ou o atual return StreamingResponse( response["Body"], media_type=response["ContentType"], # Tipo de arquivo, ex.: image/jpeg headers={"Content-Disposition": f'attachment; filename="{original_filename}"'} ) except ClientError as e: if e.response["Error"]["Code"] == "NoSuchKey": raise HTTPException(status_code=404, detail="File not found") raise HTTPException(status_code=500, detail="Error accessing file")