原理:简单来说,就是将前端需要上传的大文件拆分成为无数个小文件进行循环上传,后端接受的时候进行判断,往同一个文件里面添加,代码如下:
前端 经测试原生Ajax 有上传限制44次得限制,使用Vue的axios组件时没有异常情况
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="file" onchange="addAnnex('uploadAnnex-2')" id="uploadAnnex-2"/>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
//上传附件,监听input框onchange时间
function addAnnex(id) {
var file = document.getElementById(id).files[0];
//调用分片上传方法
PostFile(file,0,id);
}
//执行分片上传
function PostFile(file,i,domId,json){
//获取上传参数
var name = file.name,//文件名
size = file.size,//文件大小
shardSize = 1024 * 1024 * 7,//设置每个分片大小,我这是7M一个
shardCount = Math.ceil(size / shardSize);//计算总片数
//容错
if(i >= shardCount){
return;
}
//计算文件截取起始位置
var start = i * shardSize;
var end = start + shardSize;
//将文件进行切片
var packet = file.slice(start, end);
//组织post表单数据
var form = new FormData();
form.append("name", name);
form.append("file", packet); //slice方法用于切出文件的一部分
form.append("total", shardCount); //总片数
form.append("index", i + 1); //当前是第几片
//我这用这个变量标记当前传入的是否为第一片,存在值不是第一片,需要将之前的保存的文件名及需要的信息添加之表单数据中
if(json){
form.append('shard_name',json.name);
form.append('id',json.id);
}
//开始上传
$.ajax({
url: baseUrl,//你的url
type: "POST",
data: form,
async:false,
timeout: 10000,
processData: false,
contentType: false,
success: function (json) {
//等于200,全部上传完成
if(json.code == 200){
//此处实现你上传完成的逻辑
}else if(json.code == 201){
//继续上传下一片,并将返回的结果加入到数据中
form = '';
i++;
PostFile(file, i,domId,json.data);
}else{
//出现错误
jqtoast(json.msg);
}
},
error: function (){
}
});
}
</script>
后端 我使用了hyperf框架,其实原理都一样
//获取上传的文件数据
$request = $this->request;
$file = $request->file('file');
//获取post删除
$data = $request->post();
if (!$request->hasFile('file')) {
return ['code'=>400,'msg'=>'文件不存在'];
}
if (!$request->file('file')->isValid()) {
return ['code'=>400,'msg'=>'文件上传失败'];
}
//限制文件大小,大于100M的不允许上传
$size = $file -> getSize();
if($size / (1024*1024) > 100){
return ['code' => 401,'msg'=>'文件太大了'];
}
// 创建上传文件夹路径
$file_path = '/mnt/Static/UploadFile/'.date('Ymd') . '/';
//判断是否传入分片文件名,不存在则表示上传第一片
if(!isset($data['shard_name'])){
//获取文件后缀
$ext = explode('.',$data['name']);
$ext = array_pop($ext);
//定义新的文件名
$name = md5('product'.time());
$name = substr($name,5,10).'.'.$ext;
@mkdir($file_path,0775,true);
//定义文件保存路径
$path = $file_path.$name;
$file->moveTo($path);
//保存文件
if(!($file->isMoved())){
return ['code' => 400,'msg'=>'文件保存失败'];
}
//组织保存数据
$result = [
'name' => $name,
'src_name' => $data['name'],
'index' => $data['index'],
'total' => $data['total'],
];
$upStatus = '初次上传';
}else{
//定义文件保存路径
$path = $file_path.$data['shard_name'];
// 将文件流追加写入文件
file_put_contents($path,$file->getStream(),FILE_APPEND);
//组织返回数据
$result = [
'name' => $data['shard_name'],
'src_name' => $data['name'],
];
$upStatus = '分片上传';
}
$arr = [
'name' => $data['name'],
'size' => $data['total'] * $file->getSize(),
'created_at' => date('Y-m-d H:i:s'),
];
if ($data['total'] == $data['index']){
$upStatus = '上传结束';
}
$msg = [
'总次数' => $data['total'],
'上传状态' => $upStatus,
'当前次数' => '第'.$data['index'].'次',
'time' => $arr['created_at'],
];
var_dump($msg);
//判断是否上传完毕
if($data['total'] == $data['index']){
//上传完毕
return ['code'=>200,'msg'=>'成功','data'=>['arr'=>$arr]];
}else{
return ['code'=>201,'msg'=>'成功','data'=>$result];
}
上传超过2MB的文件时需要修改Hyperf框架的config/autoload/server.php的settings选项,修改限制,如下下面,将上传限制修改为10MB
Constant::OPTION_SOCKET_BUFFER_SIZE => 10 * 1024 * 1024,
Constant::OPTION_BUFFER_OUTPUT_SIZE => 10 * 1024 * 1024,
Constant::OPTION_PACKAGE_MAX_LENGTH => 10 * 1024 * 1024,