将多文件作为单文件分片上传至S3存储时的数据丢失问题
问题排查:S3分片上传后文件仅保留第一部分内容
我尝试将多个独立文件作为单个文件的分片上传至S3存储,最终生成的合并文件显示大小正确,但仅保留了第一个文件第一页的部分内容,其余数据均丢失。
注意:我无法先将所有文件合并为一个文件再进行分片上传
我的代码
import os import math import boto3 def upload_to_ovh_part_files(bucket, parts_folder): """ Fonction pour uploader des fichiers en morceaux sur OVH Object Storage. :param bucket: Le nom du bucket sur OVH :param parts_folder: Le chemin du dossier contenant les parties à uploader """ s3 = boto3.client( 's3', aws_access_key_id='my_key', aws_secret_access_key='my-sak', endpoint_url='https:...', ) parts = [] upload_id = None docx_files = [f for f in os.listdir(parts_folder) if f.endswith('.docx')] print(f'Found {len(docx_files)} parts to upload.') if docx_files: mp = s3.create_multipart_upload(Bucket=bucket, Key='rapport_final.docx') upload_id = mp['UploadId'] try: for i, docx_file in enumerate(docx_files, start=1): print(f'upload de {i} file ') file_path = os.path.join(parts_folder, docx_file) file_size = os.path.getsize(file_path) print(f'Uploading part {i}: {docx_file} (size: {file_size / (1024 * 1024):.2f} MB)') with open(file_path, 'rb') as f: data = f.read() part = s3.upload_part( Bucket=bucket, Key='rapport_final.docx', PartNumber=i, UploadId=upload_id, Body=data ) parts.append({ 'ETag': part['ETag'], 'PartNumber': i }) s3.complete_multipart_upload( Bucket=bucket, Key='rapport_final.docx', UploadId=upload_id, MultipartUpload={'Parts': parts} ) print("Upload completed successfully!") except Exception as e: print(f"Error during upload: {e}") if upload_id: s3.abort_multipart_upload(Bucket=bucket, Key='rapport_final.docx', UploadId=upload_id) else: print("No DOCX files found to upload.") parts_folder = "rapports" bucket_name = "mon-bucket-rapport" upload_to_ovh_part_files(bucket_name, parts_folder)
问题原因与解决方案
核心问题
- 文件顺序无保证:
os.listdir()返回的文件顺序由操作系统文件系统决定,并非固定的逻辑顺序。如果分片文件未按正确顺序上传,合并后的文件结构混乱,后续内容无法被docx解析。 - docx文件不能直接拼接:docx是结构化的ZIP压缩包,包含多个XML和资源文件。直接将多个独立docx文件作为分片拼接,会生成无效的ZIP结构——只有第一个文件的ZIP头有效,后续内容被视为无效数据,因此仅第一部分可被解析。
修正方案
- 强制分片顺序:给分片文件命名时加入明确序号(如
part_1.dat、part_2.dat),上传前对文件名排序:# 替换原docx_files获取逻辑,按序号排序 docx_files = sorted([f for f in os.listdir(parts_folder) if f.endswith('.docx')], key=lambda x: int(x.split('_')[1].split('.')[0])) - 调整分片内容:如果你的"分片文件"是从同一个大docx拆分的二进制块,确保顺序即可;如果是独立docx文件,无法通过分片上传拼接成有效docx——这种情况建议直接上传多个独立文件,或放弃分片上传逻辑。
内容的提问来源于stack exchange,提问作者Akram H
相关产品推荐
相关产品推荐

