大QBW文件分片上传异常求助:类型为空、合并后0KB且格式错误
问题排查与修复方案
核心问题分析
1. 文件类型为空
QBW(QuickBooks文件)属于浏览器无法识别的小众格式,file.type会返回空字符串,这是浏览器的正常行为,需手动指定或通过扩展名推导。
2. 合并后文件异常(0KB/格式错误)
前端分片读取、传输逻辑错误,后端接收二进制数据的方式完全错误,导致实际未正确写入文件内容。
前端代码修复
关键修复点
- 正确读取分片Blob:每次读取当前分片的
blob而非整个文件 - 用FormData传输二进制数据:避免jQuery自动编码破坏二进制内容
- 修正分片切割范围:
slice结束位置应为loadedSize + chunkSize,而非固定值 - 手动指定QBW文件类型:浏览器无法识别该格式,直接设置为
application/octet-stream
修改后的前端代码:
chunky: function(file, loadedSize) { const chunkSize = 1048576; // 1MB分片 const totalFileSize = file.size; const noOfChunks = Math.ceil(totalFileSize / chunkSize); let reader = new FileReader(); let requestNumber = loadedSize === 0 ? 1 : Math.floor(loadedSize / chunkSize) + 1; // 定义读取分片的方法 function readNextChunk() { const start = (requestNumber - 1) * chunkSize; const end = Math.min(start + chunkSize, totalFileSize); const blob = file.slice(start, end); reader.readAsArrayBuffer(blob); } reader.onloadend = function(event) { if (event.target.readyState !== FileReader.DONE) return; if (!event.target.result) return; // 手动处理文件类型,QBW浏览器无法识别 const fileType = file.type || 'application/octet-stream'; // 用FormData封装二进制数据 const formData = new FormData(); formData.append('bloby', new Blob([event.target.result]), file.name); formData.append('fileName', file.name); formData.append('fileType', fileType); formData.append('fileSize', totalFileSize); formData.append('loadedSize', (requestNumber - 1) * chunkSize); formData.append('requestNumber', requestNumber); jQuery.ajax({ url: IntegrationServer.path + '/uploadfileInChuncks?' + jQuery.param(Integration.addfile.requestParams), type: "POST", data: formData, processData: false, // 禁止jQuery处理数据 contentType: false // 让浏览器自动设置正确的Content-Type }).done(function(response) { console.log('分片上传完成:', response); const progress = Math.min(requestNumber / noOfChunks * 100, 100); jQuery('.upload_rpogress').html(`Uploaded: ${progress.toFixed(1)}%`); if (requestNumber < noOfChunks) { requestNumber++; readNextChunk(); } else { console.log('所有分片上传完成,开始合并'); jQuery('.upload_rpogress').html('Uploaded: 100%'); Integration.addfile.mergeFile(); } }).error(function(xhr, status, errorThrown) { jQuery('#errorMessage').html(xhr.responseText); }); }; // 启动第一个分片读取 readNextChunk(); }
后端PHP代码修复
关键修复点
- 正确接收二进制分片:通过
$_FILES获取上传的文件内容,而非从getParams读取 - 直接写入二进制数据:无需base64编码,避免额外编码导致文件损坏
- 保留分片文件用于合并:不删除已存在的分片,确保每个分片独立存储
修改后的后端代码:
function uploadFileinChuncks($request, $response, $args) { try { // 获取上传的分片文件 $chunkFile = $_FILES['bloby'] ?? null; if (!$chunkFile || $chunkFile['error'] !== UPLOAD_ERR_OK) { throw new Exception('分片文件上传失败'); } $fileName = $_POST['fileName'] ?? ''; $loadedSize = $_POST['loadedSize'] ?? 0; $requestNumber = $_POST['requestNumber'] ?? 0; $folderName = self::TEMP_FOLDER . self::QB_FOLDER; // 创建临时目录 if (!is_dir($folderName)) { mkdir($folderName, 0777, true); chmod($folderName, 0777); } // 分片文件名:用序号+原文件名,确保顺序正确 $destinationFile = $folderName . '/' . $requestNumber . '-' . $fileName; // 将上传的临时文件移动到目标位置 if (!move_uploaded_file($chunkFile['tmp_name'], $destinationFile)) { throw new Exception('无法保存分片文件'); } return $response->write($destinationFile); } catch (\Exception $e) { return $response->withStatus(500)->write($e->getMessage()); } }
合并文件补充逻辑
在mergeFile方法中需完成以下操作:
- 按分片序号从小到大排序所有分片文件
- 依次读取每个分片内容,写入最终QBW文件
- 删除所有临时分片文件
示例合并逻辑(PHP):
function mergeFile($fileName) { $folderName = self::TEMP_FOLDER . self::QB_FOLDER; $finalFile = self::UPLOAD_FOLDER . '/' . $fileName; // 获取所有分片文件并按序号排序 $chunkFiles = glob($folderName . '/*-' . $fileName); usort($chunkFiles, function($a, $b) { $numA = explode('-', basename($a))[0]; $numB = explode('-', basename($b))[0]; return (int)$numA - (int)$numB; }); // 合并文件 $fp = fopen($finalFile, 'wb'); foreach ($chunkFiles as $chunk) { fwrite($fp, file_get_contents($chunk)); unlink($chunk); // 删除临时分片 } fclose($fp); return $finalFile; }
内容的提问来源于stack exchange,提问作者Roxana Sarbu
相关产品推荐
相关产品推荐

