FastAPI集成S3时用boto3删除对象遇Access Denied错误求助
AWS S3 DeleteObject Access Denied 问题排查与修复
问题描述
将AWS S3集成到FastAPI项目中,已成功配置IAM权限和S3桶权限实现图片上传,但执行删除图片操作时反复出现Access Denied错误,已排查桶名/密钥拼写、版本控制、对象存在性等问题,仍未解决。
报错详情
INFO: 127.0.0.1:56293 - "DELETE /api/feed/delete/5 HTTP/1.1" 500 Internal Server Error ERROR: Exception in ASGI application Traceback (most recent call last): File "/opt/homebrew/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 408, in run_asgi result = await app( # type: ignore[func-returns-value] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__ return await self.app(scope, receive, send) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/fastapi/applications.py", line 292, in __call__ await super().__call__(scope, receive, send) File "/opt/homebrew/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__ await self.middleware_stack(scope, receive, send) File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__ raise exc File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__ raise exc File "/opt/homebrew/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__ await self.app(scope, receive, sender) File "/opt/homebrew/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__ raise e File "/opt/homebrew/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__ await self.app(scope, receive, send) File "/opt/homebrew/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__ await route.handle(scope, receive, send) File "/opt/homebrew/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle await self.app(scope, receive, send) File "/opt/homebrew/lib/python3.11/site-packages/starlette/routing.py", line 66, in app response = await func(request) ^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/fastapi/routing.py", line 273, in app raw_response = await run_endpoint_function( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/fastapi/routing.py", line 192, in run_endpoint_function return await run_in_threadpool(dependant.call, **values) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/starlette/concurrency.py", line 41, in run_in_threadpool return await anyio.to_thread.run_sync(func, *args) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/anyio/to_thread.py", line 33, in run_sync return await get_asynclib().run_sync_in_worker_thread( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 877, in run_sync_in_worker_thread return await future ^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 807, in run result = context.run(func, *args) ^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/gnimom/Documents/Personal/FastAPI_SQLAlchemy_Pydantic/FastAPI_SQLAlchemy_Pydantic/routers/feed_router.py", line 70, in delete feed_service.delete_feed(db, feed_id, email) File "/Users/gnimom/Documents/Personal/FastAPI_SQLAlchemy_Pydantic/FastAPI_SQLAlchemy_Pydantic/services/feed_service.py", line 161, in delete_feed delete_image_from_s3(image_url) File "/Users/gnimom/Documents/Personal/FastAPI_SQLAlchemy_Pydantic/FastAPI_SQLAlchemy_Pydantic/services/feed_service.py", line 190, in delete_image_from_s3 s3_client.delete_object(Bucket=settings.S3_BUCKET, Key=image_name) File "/opt/homebrew/lib/python3.11/site-packages/botocore/client.py", line 535, in _api_call return self._make_api_call(operation_name, kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/homebrew/lib/python3.11/site-packages/botocore/client.py", line 980, in _make_api_call raise error_class(parsed_response, operation_name) botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the DeleteObject operation: Access Denied
当前配置
S3桶策略
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Statement1", "Effect": "Allow", "Principal": "*", "Action": "s3:*", "Resource": [ "arn:aws:s3:::my-fastapi", "arn:aws:s3:::my-fastapi/*" ] } ] }
IAM与客户端代码
S3客户端初始化
import boto3 from config import settings s3_client = boto3.client( "s3", aws_access_key_id=settings.S3_ACCESS_KEY, aws_secret_access_key=settings.S3_SECRET_KEY, region_name="ap-northeast-2", )
删除图片函数
def delete_image_from_s3(image_url: str): image_name = image_url.split("/")[-1] bucket_name = settings.S3_BUCKET logging.debug(f"Deleting from Bucket: {bucket_name}, Key: {image_name}") s3_client.delete_object(Bucket=settings.S3_BUCKET, Key=image_name)
排查与修复方案
- 验证对象Key的完整性:
image_url.split("/")[-1]可能丢失对象的前缀路径(比如上传时Key是uploads/xxx.jpg,但拆分后仅得到xxx.jpg),导致请求的对象不存在,S3会返回Access Denied混淆问题。打印完整的Key,对比S3控制台中的对象实际路径确认。 - 检查桶的Block Public Access设置:即使桶策略开放全权限,Block Public Access中的
Block public access to buckets and objects granted through any access control lists (ACLs)等选项可能覆盖权限配置,需确认该设置未限制删除操作。 - 确认IAM用户的权限边界:若IAM用户附加了权限边界,即使配置了
S3FullAccess,权限边界可能限制DeleteObject操作,需检查权限边界策略是否包含该操作。 - 检查S3 Object Lock状态:若桶启用了Object Lock,对象处于Retention或Legal Hold锁定状态时无法删除,需在控制台查看对象的锁定配置。
- 用AWS CLI验证权限:使用对应IAM凭证执行命令
aws s3 rm s3://my-fastapi/[完整对象Key] --region ap-northeast-2,直接测试删除操作,排除代码层面的问题。 - 确认桶区域匹配:确保代码中指定的
ap-northeast-2与S3桶实际创建的区域一致,区域不匹配会导致请求被拒绝。
内容的提问来源于stack exchange,提问作者Taeyoung
相关产品推荐
相关产品推荐

