实现思路
前端使用js切分文件blob对象,使用ajax一次一个切片按顺序上传到后端接口,后端发现文件片数为等于总的片数时,将切片合并。
遇到问题
上传过程中需要修改php.ini配置,ngnix配置,以支持大文件,多文件上传
>nginx配置
1.client_max_body_size 2048M;
2.fastcgi_read_timeout 600s;
3.fastcgi_send_timeout 600s;
4.fastcgi_connect_timeout 600s;
>php.ini配置
1.memory_limit 2048M
2.post_max_size 2048M
3.upload_max_filesize 2018M
4.max_file_uploads 100
前端代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#progress{
width: 300px;
height: 20px;
background-color:#f7f7f7;
box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);
border-radius:4px;
background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);
}
#finish{
background-color: #149bdf;
background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
background-size:40px 40px;
height: 100%;
}
form{
margin-top: 50px;
}
</style>
</head>
<body>
<div id="progress">
<div id="finish" style="width: 0%;" progress="0"></div>
</div>
<form action="./upload.php">
<input type="file" name="file" id="file">
</form>
<script>
var fileForm = document.getElementById("file");
var stopBtn = document.getElementById('stop');
fileForm.onchange = function(){
var upload = new Upload();
upload.addFileAndSend(this);
}
function Upload(){
var xhr = new XMLHttpRequest();
var form_data = new FormData();
const LENGTH = 1024 * 1024;
var start = 0;
var end = start + LENGTH;
var blob;
var blob_num = 1;
//对外方法,传入文件对象
this.addFileAndSend = function(that){
var file = that.files[0];
blob = cutFile(file);
sendFile(blob,file);
blob_num += 1;
}
//切割文件
function cutFile(file){
var file_blob = file.slice(start,end);
start = end;
end = start + LENGTH;
return file_blob;
};
//发送文件
function sendFile(blob,file){
var total_blob_num = Math.ceil(file.size / LENGTH);
form_data.append('file',blob);
form_data.append('blob_num',blob_num);
form_data.append('total_blob_num',total_blob_num);
form_data.append('file_name',file.name);
xhr.open('POST','./upload.php',false);
xhr.onreadystatechange = function () {
var progress;
var progressObj = document.getElementById('finish');
if(total_blob_num == 1){
progress = '100%';
}else{
progress = Math.min(100,(blob_num/total_blob_num)* 100 ) +'%';
}
progressObj.style.width = progress;
var t = setTimeout(function(){
if(start < file.size){
blob = cutFile(file);
sendFile(blob,file);
blob_num += 1;
}else{
setTimeout(t);
}
},1000);
}
xhr.send(form_data);
}
}
</script>
</body>
</html>
后端PHP代码
<?php
class Upload{
private $tmpFile; //临时文件目录
private $blobNum; //第几个文件块
private $totalBlobNum; //文件块总数
private $fileName; //文件名
private $mimeType; //文件类型
private $splitFilepath; //文件存放位置
public function __construct(){
}
/**
* 将大文件切片上传
* @param Request $request
* @return Json
*/
public function splitUpload(Request $request){
$this->tmpFile = $_FILES['file']['tmp_name'];
$this->blobNum = $request->param('blob_num');
$this->totalBlobNum = $request->param('total_blob_num');
$this->fileName = $request->param('file_name');
$this->mimeType = $request->param('mime_type');
$this->splitFilepath = rtrim($_SERVER['DOCUMENT_ROOT'], 'public') . 'tmpfile';
$this->moveFile();
//合并
if($this->blobNum == $this->totalBlobNum) {
$res = $this->fileMerge();
return response_success('allTrue', $res);
} else {
return response_success('True');
}
}
/**
* 将缓存文件移动到特定目录
* @return Json
*/
private function moveFile(){
$this->touchDir();
$filename = $this->splitFilepath.'/'. $this->fileName.'__'.$this->blobNum;
if (file_exists($filename)) {
return response_success('fileExists');
}
move_uploaded_file($this->tmpFile, $filename);
}
/**
* 建立上传的文件
* @return bool
*/
private function touchDir(){
$this->splitFilepath .= '/' . date('Ymd', time());
if(!file_exists($this->splitFilepath)){
return mkdir($this->splitFilepath);
}
}
/**
* 判断是否是最后一块,如果是则进行文件合成并且删除文件块并上传到阿里云
* @return array|Json
*/
private function fileMerge(){
//修改临时内存
ini_set('memory_limit','3072M'); // 临时设置最大内存占用为3G
set_time_limit(0); // 设置脚本最大执行时间 为0 永不过期
$blob = '';
for($i=1; $i<= $this->totalBlobNum; $i++){
$blob .= file_get_contents($this->splitFilepath.'/'. $this->fileName.'__'.$i);
}
file_put_contents($this->splitFilepath.'/'. $this->fileName,$blob);
//将切片删除
$this->deleteFileBlob();
}
/**
* 删除本地的切片文件
*/
private function deleteFileBlob(){
for($i=1; $i<= $this->totalBlobNum; $i++){
@unlink($this->splitFilepath.'/'. $this->fileName.'__'.$i);
}
}
网友评论