首页 > 解决方案 > 如何从 FastAPI 中的 UploadFile 获取文件路径?

问题描述

基本上,我正在尝试创建一个端点以将文件上传到 S3

async def upload_files(filepath: str, upload_file_list: List[UploadFile] = File(...)):
    for upload_file in upload_file_list:
        abs_file_path = "/manual/path/works" + upload_file.path
        # Replace above line to get absolute file path from UploadFile
        response = s3_client.upload_file(abs_file_path,bucket_name,
                                                   os.path.join(dest_path, upload_file.filename))

以上是我将多个文件上传到 S3 存储桶的代码。 s3_client.upload_file()接受要上传的文件的绝对文件路径。当我手动放置完整路径时它正在工作。

这没有用:

response = s3_client.upload_file(upload_file.filename, bucket_name,
                                                   os.path.join(dest_path, upload_file.filename))

有没有办法让这个绝对路径进入FastAPItemp_path或者没有复制或写入文件的任何替代方案?

如果没有,那么使用任何替代方法boto3将文件上传到 S3FastAPI

标签: pythonamazon-s3boto3fastapi

解决方案


UploadFile 使用 Python 的SpooledTemporaryFile,它是“存储在内存中的文件”,并且“一关闭就销毁”。您可以读取文件内容(i.e., contents = await file.read()),然后将这些字节上传到您的服务器(如果允许),或者将上传文件的内容复制到NamedTemporaryFile中,如此所述。与 SpooledTemporaryFile 不同,NamedTemporaryFile “保证在文件系统中有一个可见的名称”,即“可以用来打开文件”。临时文件的路径可以通过file_copy.name

contents = await file.read()
file_copy = NamedTemporaryFile('wb', delete=False)
f = None
try:
    # Write the contents to the temp file
    with file_copy as f:
        f.write(contents);

    # Here, upload the file to your S3 service
    f = open(file_copy.name, 'rb')
    print(f.read(10))

finally:
    if f is not None:
        f.close() # Remember to close the file
    os.unlink(file_copy.name)  # delete the file

更新

此外,可以使用该file属性访问实际的 Python 文件。根据文档

file: 一个SpooledTemporaryFile(一个类似文件的对象)。这是您可以直接传递给需要“类文件”对象的其他函数或库的实际 Python 文件。

因此,您也可以尝试使用upload_fileobj函数并传递upload_file.file

 response = s3_client.upload_fileobj(upload_file.file, bucket_name, os.path.join(dest_path, upload_file.filename))

或者,使用 SpooledTemporaryFile 的属性传递一个类似文件的对象,该属性_file返回一个io.BytesIOio.TextIOWrapper对象(取决于是否指定了二进制或文本模式)。

 response = s3_client.upload_fileobj(upload_file.file._file, bucket_name, os.path.join(dest_path, upload_file.filename))

更新 2

您甚至可以将字节保存在内存缓冲区BytesIO中,使用它将内容上传到 S3 存储桶,最后关闭它(“调用close()方法时会丢弃缓冲区。”)。请记住在完成对 BytesIO 流的写入后调用seek(0)方法将光标重置回文件的开头。

contents = await file.read()
temp_file = io.BytesIO()
temp_file.write(contents)
temp_file.seek(0)
s3_client.upload_fileobj(temp_file, bucket_name, os.path.join(dest_path, upload_file.filename))
temp_file.close()

推荐阅读