如何从S3下载文件到本地后通过POST请求发送及相关报错解决
方案可行性说明
你提出的「先将S3文件下载到本地、再读取本地文件发起POST请求」的方案完全可行,适合绝大多数中小体积文件的传输场景,实现逻辑简单稳定。如果需要处理GB级以上大文件,可以优化为流式读写避免占用本地磁盘空间,普通场景下直接用你的思路即可。
现有代码错误修正
核心错误点梳理
- S3路径拆分错误:S3 URI
s3://qq-dag/production/special-db/xx.gz中,qq-dag才是存储桶(bucket)名称,production/special-db/xx.gz是完整的对象键(key),你之前错误把路径前缀放到了bucket参数中,导致资源定位失败。 - 语法错误:POST部分的文件读取函数写错为
ocpen,存在多余的单引号导致字符串解析失败,未定义的payload变量会引发报错,gz文件的MIME类型配置错误,正确应为application/gzip。 - 密钥配置错误:你调整后的代码把AWS密钥填反了:
aws_access_key_id参数需要填入AKIA开头的访问密钥ID,aws_secret_access_key填入另外一长串的秘密访问密钥,填反会直接导致鉴权失败,触发400错误。
你遇到的报错
ClientError: An error occurred (400) when calling the HeadObject operation: Bad Request中文翻译为:客户端错误:调用HeadObject操作时发生错误(400):错误请求,该错误绝大多数场景是由鉴权信息错误、存储桶/对象键配置错误、区域配置和桶实际区域不匹配三类问题导致的。
可运行实现示例
import boto3 import requests # 基础配置 S3_URI = "s3://qq-dag/production/special-db/xx.gz" LOCAL_FILE_PATH = r"C:\Users\jhon.king\files\xx.gz" POST_URL = "替换为你实际的POST接口地址" # AWS密钥建议从环境变量或者配置文件读取,不要硬编码在代码中 AWS_ACCESS_KEY_ID = "替换为你的AKIA开头的访问密钥ID" AWS_SECRET_ACCESS_KEY = "替换为你的秘密访问密钥" AWS_REGION = "替换为你的S3桶实际所在区域,例如us-east-1" # 1. 拆分S3路径获取bucket和key path_parts = S3_URI.replace("s3://", "").split("/", 1) bucket_name = path_parts[0] object_key = path_parts[1] # 2. 初始化S3客户端下载文件 s3_client = boto3.client( 's3', region_name=AWS_REGION, aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY ) s3_client.download_file(bucket_name, object_key, LOCAL_FILE_PATH) # 3. 发起POST请求上传文件 headers = { 'X-Atlassian-Token': 'nocheck', 'Authorization': 'Basic Ymyb3dpdHo6kkczEyMzQ=' } files = [ ('file', ('xx.gz', open(LOCAL_FILE_PATH, 'rb'), 'application/gzip')) ] # 如果你没有额外的表单参数不需要传data,直接删除payload参数即可 response = requests.request("POST", POST_URL, headers=headers, files=files) # 打印返回结果排查问题 print(response.status_code) print(response.text)
可选优化(无需存储本地文件)
如果不想占用本地磁盘空间,可以直接把S3文件流传递给POST请求,不需要写入本地:
# 省略前面的配置和S3客户端初始化部分 s3_response = s3_client.get_object(Bucket=bucket_name, Key=object_key) file_stream = s3_response['Body'] files = [ ('file', ('xx.gz', file_stream, 'application/gzip')) ] response = requests.request("POST", POST_URL, headers=headers, files=files)
内容的提问来源于stack exchange,提问作者Boyuis
相关产品推荐
相关产品推荐

