FastAPI上传到S3

7

我已经纠结了好几天,但仍无法解决问题。基本上,我只是想将一个 CSV 文件放在 LocalStack S3 中,但我无法让它正常工作。

这是我的代码片段:

api.py

from files import s3, AWS_S3_BUCKET_NAME, upload_file_to_bucket
    
@router.post('/api/customer/generate/upload',
                 name='Upload CSV to AWS S3 Bucket',
                 status_code=201)
    async def post_upload_user_csv(file_obj: UploadFile = File(...)):
        upload_obj = upload_file_to_bucket(s3_client=s3(),
                                           file_obj=file_obj.file,
                                           bucket=AWS_S3_BUCKET_NAME,
                                           folder='CSV',  # To Be updated
                                           object_name=file_obj.filename)
        if upload_obj:
            return JSONResponse(content="Object has been uploaded to bucket successfully",
                                status_code=status.HTTP_201_CREATED)
        else:
            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                                detail="File could not be uploaded")

files.py

import os
import boto3
import logging

from botocore.client import BaseClient
from botocore.exceptions import ClientError

AWS_ACCESS_KEY_ID = os.getenv('POSTGRES_HOST')
AWS_SECRET_KEY = os.getenv('AWS_SECRET_KEY')
AWS_S3_BUCKET_NAME = os.getenv('AWS_S3_BUCKET_NAME')


def s3() -> BaseClient:
    client = boto3.client(service_name='s3',
                          aws_access_key_id=AWS_ACCESS_KEY_ID,
                          aws_secret_access_key=AWS_SECRET_KEY,
                          endpoint_url='http://localhost:4566/')  # Use LocalStack Endpoint

    return client


def upload_file_to_bucket(s3_client, file_obj, bucket, folder, object_name=None):
    """Upload a file to an S3 bucket

    :param s3_client: S3 Client
    :param file_obj: File to upload
    :param bucket: Bucket to upload to
    :param folder: Folder to upload to
    :param object_name: S3 object name. If not specified then file_name is used
    :return: True if file was uploaded, else False
    """
    # If S3 object_name was not specified, use file_name
    if object_name is None:
        object_name = file_obj

    # Upload the file
    try:
        # with open("files", "rb") as f:
        s3_client.upload_fileobj(file_obj, bucket, f"{folder}/{object_name}")
    except ClientError as e:
        logging.error(e)
        return False
    return True

问题在于 s3_client 需要先以二进制模式打开文件,然后才能将其上传到 s3 存储桶中。但是这不能直接完成,文件需要暂时保存在 FastAPI 服务器上,但出于明显的原因,我真的不想这样做。
非常感谢您的帮助,提前感谢!

可以先将文件写入io.BytesIO吗? - fishstix44
@fishstix44抱歉造成困惑。请纠正我逻辑上的错误。当然,将文件写入变量比保存在磁盘上要好得多。据我所知,UploadFile是一个类似文件的对象,这意味着它被暂时存储在内存中,而不是直接存储在磁盘上。我试图做的是分块或异步上传提供的文件直接到S3,尽可能地实现上传。 - user2627114
1个回答

6

请先展示错误信息。

基本上,我之前做过这个。直接从前端上传文件到aws-s3。您可以尝试在upload_fileobj中添加ContentType。以下是我的代码:

 content_type = mimetypes.guess_type(fpath)[0]
 s3.Bucket(bucket_name).upload_fileobj(Fileobj=file, Key=file_path,
                                          ExtraArgs={"ACL": "public-read",
                                                     "ContentType": content_type})

另一种方法是将文件转换为 io.BytesIO

 def s3_upload(self, file, file_path, bucket_name, width=None, height=None, make_thumb=False, make_cover=False):
    s3 = boto3.resource(service_name='s3')
    obj = BytesIO(self.image_optimize_from_buffer(file, width, height, make_thumb, make_cover))
    s3.Bucket(bucket_name).upload_fileobj(Fileobj=obj, Key=file_path,
                                          ExtraArgs={"ACL": "public-read", "ContentType": file.content_type})
    return f'https://{bucket_name}.s3.amazonaws.com/{file_path}'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接