- 前端页面将文件分块上传。
- 后端在最后一条时进行合并处理。
- 最后一条需要等待前面的上传完毕再执行,因此用
Promise.all()
函数处理。
前端js
function getRandChars(e) {
e = e || 32;
var t = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678",
a = t.length,
n = "";
for (i = 0; i < e; i++) n += t.charAt(Math.floor(Math.random() * a));
return n
}
function uploadFile(url, blob, callback) {
var perFileSize = 2097152; // 2 * 1024 * 1024
var blobParts = Math.ceil(blob.size / perFileSize);
const fileName = getRandChars();
layer.msg("文件上传中", {
icon: 16,
time: -1
})
function request(url, fd, callback) {
return new Promise((resolve, reject) => {
$.ajax({
type: 'POST',
url: url,
data: fd,
processData: false,
contentType: false
}).done(function (res) {
resolve(res);
}).fail(function (err) {
reject(err);
});
});
}
let beforeRequests = []
for (var i = 0; i < blobParts; i++) {
(function (i) {
var fd = new FormData();
var _blob = blob.slice(i * perFileSize, (i + 1) * perFileSize);
fd.append('file', _blob);
fd.append('fileName', fileName);
fd.append('index', i + 1);
fd.append('total', blobParts);
if (i + 1 < blobParts) {
beforeRequests.push(request(url, fd, callback))
} else {
Promise.all(beforeRequests).then(responses => {
// 所有请求完成后执行的操作
request(url, fd, callback)
.then(res => {
layer.closeAll()
if (res.status == 0) {
layer.msg(res.mes)
} else if (res.object.status === 200) {
callback(null, res.object)
} else if (res.object.status === 500) {
layer.msg(res.object.msg)
}
})
}).catch(error => {
layer.closeAll()
callback(error, null)
}).finally(() => {
});
}
})(i)
}
}
php 部分
class PartUploadLogic
{
protected $blobNum;
protected $totalBlobNum;
public function UploadInChunks()
{
set_time_limit(600);
ini_set('memory_limit', '3000M');
$file = $_FILES['file'];
$this->blobNum = I('index/d', 1);
$this->totalBlobNum = I('total/d', 1);
$fileName = I("fileName/s");
$endPath = '/uploads/file';
$tmpPath = $endPath . 'tmp/';
$savePath = date("Ymd");
$tmpRelativePath = "$tmpPath{$savePath}";
$tmpSavePath = realpath('') . $tmpRelativePath . "/$fileName";
if (!is_dir($tmpSavePath)) {
mkdir($tmpSavePath, 755, true);
}
$saveName = $fileName . '_' . $this->blobNum;
move_uploaded_file($file['tmp_name'], "$tmpSavePath/$saveName");
// 合并上传的子文件
if ($this->totalBlobNum == $this->blobNum) {
if ($this->totalBlobNum > 1) {
sleep(2);
}
try {
$endRelativePath = "$endPath/$savePath";
$finishSavePath = realpath('') . $endRelativePath;
if (!is_dir($finishSavePath)) {
mkdir($finishSavePath, 755, true);
}
$fp = fopen("{$finishSavePath}/{$fileName}.pdf", "w+");
// 合并文件
for ($i = 1; $i <= $this->totalBlobNum; $i++) {
$value = "$tmpSavePath/{$fileName}_{$i}";
$handle = fopen($value, "rb");
if (empty($handle)) {
failData('读取不到分包数据');
}
fwrite($fp, fread($handle, filesize($value)));
fclose($handle);
unset($handle);
@unlink($value);
}
del_dir($tmpSavePath);
} catch (\Exception $exception) {
return ['status' => 500, 'msg' => "文件合并失败{$exception->getLine()}{$exception->getMessage()}}"];
}
return ['status' => 200, 'msg' => 'success', 'path' => "$endRelativePath/{$fileName}.pdf"];
}
return ['status' => 201, 'msg' => 'success', 'fileName' => $fileName];
}
}