如何为AWS Glue输出文件指定自定义名称并添加.JSON扩展名?
这问题我之前帮好几个同行解决过,AWS Glue默认的输出命名确实有点死板——随机后缀还不带.json扩展名,不过有几个靠谱的办法能搞定,我给你拆解清楚:
方法一:在Glue作业中直接控制输出(最推荐)
Glue底层基于Spark,所以我们可以用Spark的API来绕开默认的命名规则,步骤很清晰:
把DynamicFrame转成Spark DataFrame
如果你用的是Glue自带的DynamicFrame,先转成DataFrame方便后续操作:from awsglue.dynamicframe import DynamicFrame # 假设你的DynamicFrame名叫dynamic_frame df = dynamic_frame.toDF()合并小文件(可选但实用)
Spark默认会按分区生成多个part文件,如果你想要单个输出文件,可以用coalesce(1)合并(注意:数据量特别大的时候别用,会把所有数据挤到一个Executor,影响性能):df = df.coalesce(1)自定义文件名写入S3
先把数据写到临时路径生成part文件,再用boto3把它重命名成你想要的名称,顺便加上.json扩展名:import boto3 # 临时路径(用来存Glue默认生成的part文件) temp_output = "s3://your-bucket/temp-folder/" # 你最终想要的文件路径和名称 final_output = "s3://your-bucket/final-folder/my-custom-file.json" # 先把数据以JSON格式写到临时路径 df.write.mode("overwrite").json(temp_output) # 用boto3找到临时路径里的part文件 s3_client = boto3.client('s3') bucket_name = "your-bucket" temp_prefix = "temp-folder/" # 列出临时路径下的所有文件 response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=temp_prefix) part_file_key = None for obj in response['Contents']: # 找到带.json后缀的part文件 if obj['Key'].endswith('.json') and 'part-' in obj['Key']: part_file_key = obj['Key'] break # 重命名并移动到最终路径 if part_file_key: # 复制part文件到目标路径 s3_client.copy_object( Bucket=bucket_name, CopySource={'Bucket': bucket_name, 'Key': part_file_key}, Key=final_output.split('/')[-1] if final_output.endswith('/') else final_output ) # 删除临时的part文件和_SUCCESS标记(可选) s3_client.delete_object(Bucket=bucket_name, Key=part_file_key) s3_client.delete_object(Bucket=bucket_name, Key=f"{temp_prefix}_SUCCESS")
方法二:让Glue自动带.json扩展名(快速解决扩展名问题)
如果只是缺.json扩展名,不想搞复杂的重命名,可以直接用Glue的write_dynamic_frame.from_options方法,指定格式为json,这样生成的part文件会自动带.json后缀:
from awsglue.job import Job from awsglue.context import GlueContext from pyspark.context import SparkContext sc = SparkContext() glueContext = GlueContext(sc) job = Job(glueContext) # 假设你的数据是dynamic_frame write_dynamic_frame.from_options( frame=dynamic_frame, connection_type="s3", connection_options={"path": "s3://your-bucket/output-folder/"}, format="json" )
不过这种方式还是会有随机后缀的part文件,适合只需要扩展名、对文件名随机后缀无所谓的场景。
方法三:用Lambda自动重命名(无侵入式)
如果不想在Glue作业里加额外代码,可以给S3桶加个事件通知,当Glue输出文件时触发Lambda自动重命名:
写Lambda函数
用Python写个简单的函数,监听S3的文件创建事件,找到新生成的part文件,改成你想要的名字:import boto3 s3 = boto3.client('s3') def lambda_handler(event, context): # 从事件里拿桶名和文件路径 bucket = event['Records'][0]['s3']['bucket']['name'] file_key = event['Records'][0]['s3']['object']['key'] # 只处理Glue生成的part文件 if file_key.startswith('output-folder/') and 'part-' in file_key and file_key.endswith('.json'): # 自定义新文件名,比如按日期命名或者固定名称 new_file_key = "output-folder/my-final-file.json" # 复制并重命名 s3.copy_object( Bucket=bucket, CopySource={'Bucket': bucket, 'Key': file_key}, Key=new_file_key ) # 删除原part文件和_SUCCESS标记 s3.delete_object(Bucket=bucket, Key=file_key) success_key = file_key.rsplit('/', 1)[0] + '/_SUCCESS' s3.delete_object(Bucket=bucket, Key=success_key)配置S3事件通知
打开你的S3输出桶,找到「事件通知」,添加一个新通知:- 事件类型选「所有对象创建事件」
- 触发目标选刚才创建的Lambda函数
- 前缀填Glue输出的路径(比如
output-folder/),避免触发无关文件的处理
一些注意事项
- 数据量很大时,别用
coalesce(1),可以用partitionBy按业务字段(比如日期)分区,每个分区生成一个文件,再批量重命名 - 如果需要动态文件名(比如每天生成一个带日期的文件),可以从DataFrame里提取日期字段,拼到文件名里
- 确保Glue作业和Lambda的IAM角色有足够的S3读写权限,不然会报错
内容的提问来源于stack exchange,提问作者Ewan Peters

