JS+PHP分片上传10MB以上视频无法播放问题排查
分片上传大视频后无法播放的问题排查与修复
核心问题:分片合并顺序错乱
你的PHP代码在合并分片时,使用glob($targetDir . $filename . '.*')获取所有分片文件,但glob返回的文件列表是字符串字典序,而非数字顺序。比如17.6MB的视频会生成18个分片,filename.10会排在filename.2前面,合并后的文件字节顺序完全混乱,导致视频无法播放——但总字节数和原文件一致,所以显示的文件大小正常。
修复代码(PHP):
替换分片获取与排序逻辑,确保按数字索引顺序合并:
// 原代码 $chunks = glob($targetDir . $filename . '.*'); // 修复后:自然排序保证数字顺序 $chunks = glob($targetDir . $filename . '.*'); natsort($chunks); // 按数字大小排序,而非字符串字典序 $chunks = array_values($chunks); // 重置数组索引,避免排序后索引混乱
次要问题:并行上传的不确定性
你的JS代码在循环中直接发送所有分片的XHR请求,属于并行上传。虽然最终所有分片都会到达服务器,但可能导致服务器接收顺序和发送顺序不一致,同时并发请求可能引发服务器资源竞争。建议改为串行上传(上传完一个分片再传下一个),避免潜在问题。
修复代码(JS):
修改processFile函数为串行上传逻辑:
function processFile() { if (!file.files[0]) { console.error("No file selected."); return; } const TheFile = file.files[0]; const filename = TheFile.name; const filesize = TheFile.size; const number_allowed_chunks = Math.ceil(filesize / chunkSize); let chunkId = 0; // 单个分片上传函数 function uploadChunk() { if (chunkId >= number_allowed_chunks) return; const start = chunkId * chunkSize; const end = Math.min(start + chunkSize, filesize); const chunk = TheFile.slice(start, end); const chunkIndex = chunkId + 1; const xrequest = new XMLHttpRequest(); xrequest.open("POST", "chatphp.php", true); xrequest.onerror = () => console.error("请求失败:分片", chunkIndex); xrequest.upload.addEventListener("error", () => console.error("上传失败:分片", chunkIndex)); xrequest.upload.addEventListener("progress", ({ loaded, total }) => { updateProgress(loaded, total, number_allowed_chunks, chunkIndex); }); // 当前分片上传完成后,启动下一个分片上传 xrequest.onload = () => { chunkId++; uploadChunk(); }; const formData = new FormData(); formData.append("chunk", chunk); formData.append("chunkIndex", chunkIndex); formData.append("fileName", filename); formData.append("totalNumber", number_allowed_chunks); xrequest.send(formData); } // 启动串行上传 uploadChunk(); }
其他潜在问题优化
- MD5校验时机调整:原代码先移动分片文件再校验,建议先校验临时文件,确认无误后再移动,避免存储损坏的分片:
// 原代码 if (move_uploaded_file($_FILES['chunk']['tmp_name'], $chunkPath)) { $uploadedChunkFile = md5_file($chunkPath); if ($uploadedChunkFile != $checksum) { echo "Error: Quality check failed"; $GoAhead = 0; } } // 修复后 $tempChunk = $_FILES['chunk']['tmp_name']; $uploadedChunkFile = md5_file($tempChunk); if ($uploadedChunkFile != $checksum) { echo "Error: Quality check failed"; $GoAhead = 0; } else { if (move_uploaded_file($tempChunk, $chunkPath)) { // 后续分片合并逻辑 } }
- PHP配置检查:确认
php.ini中的post_max_size、upload_max_filesize均大于单分片大小(1MB),大文件上传时串行上传可避免触发max_input_vars等并发限制。
内容的提问来源于stack exchange,提问作者Theophilus Tetteh
相关产品推荐
相关产品推荐

