如何将Content-Range请求字节拼接为可播放MP4?代码问题排查
问题分析与修复方案
你的实现存在几个关键问题,直接导致MP4文件拼接后无法播放:
1. 临时文件排序逻辑错误
当前代码通过字符串排序临时文件名,比如temporary_file_1000_1999会排在temporary_file_0_999前面(字符串比较中"1" > "0"),导致文件片段拼接顺序完全错乱。MP4是结构严格的二进制文件,顺序错误会直接破坏其解析结构。
2. 临时文件写入模式错误
save_to_temporary_file里用了ab追加模式,如果同一个片段因网络重试重复上传,会导致该片段数据被多次写入,最终拼接后的文件数据冗余,破坏MP4格式。
3. 缺少基础错误校验
比如parse_content_range返回None时未处理、uploaded_file为空的情况未校验,这些都会引发后续逻辑异常。
修正后的代码
核心修改点:
- 按片段起始字节的数值排序临时文件,确保拼接顺序正确
- 临时文件改用
wb模式写入,避免重复数据 - 添加必要的参数校验,拦截非法请求
@app.route('/admin/upload', methods=['POST']) @app_key def admin_dashboard_upload_endpoint(): # Headers file_name = request.headers.get('Filename') title = request.headers.get('Title') path = f'something/path' # 校验必要参数 if not file_name: return "Missing Filename header", 400 # Get the Content-Range header content_range = request.headers.get('Content-Range') if not content_range: return "Missing Content-Range header", 400 # Parse the Content-Range header to get start and end bytes start_byte, end_byte, total_size = parse_content_range(content_range) if start_byte is None or end_byte is None or total_size is None: return "Invalid Content-Range format", 400 # Get the uploaded file from the request uploaded_file = request.files.get('File') if not uploaded_file: return "Missing File part", 400 # Save the received bytes to a temporary file os.makedirs('uploads', exist_ok=True) save_to_temporary_file(uploaded_file, start_byte, end_byte) # Check if all parts have been received if end_byte == total_size - 1: # If all parts are received, combine the temporary files into one combine_temporary_files(file_name) with open(f'uploads/{file_name}', 'rb') as combined_file: return upload_to_storage_dashboard(combined_file, title=title, path=path, file_remove=f'uploads/{file_name}') return "OK", 206 def parse_content_range(content_range): # Example of content_range: 'bytes 0-999/2000' match = re.match(r'bytes (\d+)-(\d+)/(\d+)', content_range) if match: start_byte, end_byte, total_size = map(int, match.groups()) return start_byte, end_byte, total_size else: # Handle invalid Content-Range header return None, None, None def save_to_temporary_file(uploaded_file, start_byte, end_byte): temp_file_path = f'uploads/temporary_file_{start_byte}_{end_byte}.mp4' # 用wb模式覆盖写入,确保每个片段数据唯一 with open(temp_file_path, 'wb') as temp_file: temp_file.write(uploaded_file.read()) def combine_temporary_files(file_name): combined_file_path = f'uploads/{file_name}' # 从文件名中解析start_byte,按数值排序 temp_files = [] for f in os.listdir('uploads'): if f.startswith('temporary_file_'): # 提取start_byte数值 parts = f.split('_') if len(parts) >=3: try: start_byte = int(parts[2]) temp_files.append((start_byte, f)) except ValueError: continue # 按start_byte从小到大排序,确保拼接顺序正确 temp_files.sort(key=lambda x: x[0]) with open(combined_file_path, 'wb') as combined_file: for _, temp_file_name in temp_files: temp_file_path = os.path.join('uploads', temp_file_name) with open(temp_file_path, 'rb') as temp_file: combined_file.write(temp_file.read()) os.remove(temp_file_path)
额外检查项
如果修正后仍无法播放,需确认Flutter端上传逻辑:
- 确保每个片段的
Content-Range与实际发送的字节范围完全匹配(比如发送0-999字节,对应原文件的前1000字节) - 确认没有重复发送或遗漏片段
- 检查上传过程中是否存在数据截断(发送的字节数与声明的范围不一致)
内容的提问来源于stack exchange,提问作者Jiehfeng
相关产品推荐
相关产品推荐

