共计 4920 个字符,预计需要花费 13 分钟才能阅读完成。
咱们在上传大文件时,可能会因为服务器的起因导致文件上传失败,文件过大时因为服务器的配置或响应事件过长导致上传文件失败,这时候咱们能够将一个大的文件分为若干块,而后分批次上传到服务端,当所有文件块上传实现后再由服务器将各个文件块整合成咱们上传的文件
一:分块上传流程:
1:由前端 js 将上传的文件信息进行切割成若干块,而后循环将若干块的文件块上传到服务端
2:服务端接管到文件块信息后保存起来,当所有文件块上传完毕后,将所有上传的文件块整合成文件并保存起来
二:实现代码:
1:HTML
<input type="file" id="file"> | |
<input type="button" id="upload" value="上传"> | |
<input type="button" id="stop" value="进行"> | |
<input type="button" id="restart" value="持续上传"> | |
上传进度:<span id="progress"></span> |
2:JS
// 获取节点 | |
var fileForm = document.getElementById("file"); | |
var uploadBtn = document.getElementById('upload'); | |
var stopBtn = document.getElementById('stop'); | |
var restartBtn = document.getElementById('restart'); | |
// 定义常量 | |
const LENGTH = 100 * 1024;// 每个上传的文件块大小 (100KB) | |
var start = 0; | |
var end = LENGTH + start; | |
var blob; | |
var is_stop = 0; | |
var blob_num = 1; | |
var file = null; | |
var upload_instance = new Upload(); | |
// 上传事件 | |
uploadBtn.onclick = function () {upload_instance.addFileAndSend(fileForm); | |
return false; | |
} | |
stopBtn.onclick = function () {upload_instance.stop(); | |
return false; | |
} | |
restartBtn.onclick = function () {upload_instance.start(); | |
return false; | |
} | |
function Upload() { | |
// 判断浏览器类型 | |
if (window.XMLHttpRequest){ | |
//IE7+, Firefox, Chrome, Opera, Safari | |
var xhr=new XMLHttpRequest();}else{ | |
//IE6, IE5 | |
var xhr=new ActiveXObject("Microsoft.XMLHTTP"); | |
} | |
// 上传文件 | |
this.addFileAndSend = function (that) {file = that.files[0]; | |
blob = cutFile(file); | |
// 上传 | |
uploadFile(blob, file); | |
blob_num += 1; | |
} | |
// 进行文件上传 | |
this.stop = function () {xhr.abort(); | |
is_stop = 1; | |
} | |
this.start = function () {uploadFile(blob, file); | |
is_stop = 0; | |
} | |
// 切割文件 | |
function cutFile(file) {var file_blob = file.slice(start, end); | |
start = end; | |
end = start + LENGTH; | |
return file_blob; | |
}; | |
// 上传文件 | |
function uploadFile(blob, file) {var form_data = new FormData(); | |
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', './test.php', false); | |
xhr.onreadystatechange = function () { | |
// 获取上传进度 | |
if (total_blob_num == 1) {progressText = '100%';} else {progressText = (Math.min(100, (blob_num / total_blob_num) * 100)).toFixed(2) + '%'; | |
} | |
var progress = document.getElementById('progress'); | |
progress.innerHTML = progressText; | |
// 循环执行上传,直到所有文件块上传实现 | |
var t = setTimeout(function () {if (start < file.size && is_stop == 0) {blob = cutFile(file); | |
uploadFile(blob, file); | |
blob_num += 1; | |
} else {// 所有文件块上传实现} | |
}, 1000); | |
} | |
xhr.send(form_data); | |
// 每次文件块上传后,清空上传信息 | |
form_data = ""; | |
} | |
} |
3:PHP
(1):上传类:
class Upload | |
{ | |
/** | |
* @var string 上传目录 | |
*/ | |
private $filepath = './upload'; // 上传目录 | |
/** | |
* @var string 块文件长期存储的地位 | |
*/ | |
private $tmpPath; | |
/** | |
* @var integer 第几个文件块 | |
*/ | |
private $blobNum; | |
/** | |
* @var integer // 文件块总数 | |
*/ | |
private $totalBlobNum; | |
/** | |
* @var string 上传文件名 | |
*/ | |
private $fileName; | |
public function __construct($tmpPath, $blobNum,$totalBlobNum,$fileName, $filepath = ''){if (!empty($filepath)) {$this->filepath = $filepath;} | |
$this->tmpPath = $tmpPath; | |
$this->blobNum = $blobNum; | |
$this->totalBlobNum = $totalBlobNum; | |
$this->fileName = $fileName; | |
// 保留文件块 | |
$this->moveFile(); | |
// 保留文件 | |
$this->fileMerge();} | |
private function fileMerge(){ | |
// 当文件块都上传后将文件块整合成文件 | |
if($this->blobNum == $this->totalBlobNum){for($i=1; $i<= $this->totalBlobNum; $i++){ | |
$blob = ''; | |
$blob = file_get_contents($this->filepath.'/'. $this->fileName.'__'.$i); | |
file_put_contents($this->filepath.'/'. $this->fileName, $blob, FILE_APPEND); | |
unset($blob); | |
} | |
// 删除文件块 | |
$this->deleteFileBlob();} | |
} | |
// 删除文件块 | |
private function deleteFileBlob(){for($i=1; $i<= $this->totalBlobNum; $i++){@unlink($this->filepath.'/'. $this->fileName.'__'.$i); | |
} | |
} | |
private function moveFile(){$this->touchDir(); | |
$filename = $this->filepath.'/'. $this->fileName.'__'.$this->blobNum; | |
// 保留文件块 | |
move_uploaded_file($this->tmpPath,$filename); | |
} | |
// 上传返回 | |
public function uploadReturn(){if($this->blobNum == $this->totalBlobNum){if(file_exists($this->filepath.'/'. $this->fileName)){ | |
return [ | |
'code' => 2, | |
'message' => 'success', | |
'file_path' => 'http://'.$_SERVER['HTTP_HOST'].str_replace('.','',$this->filepath).'/'. $this->fileName,'local_path'=> str_replace('.','',$this->filepath).'/'. $this->fileName | |
]; | |
} | |
} | |
return [ | |
'code' => 1, | |
'message' => 'waiting', | |
]; | |
} | |
/** | |
* 创立目录 | |
*/ | |
private function touchDir(){if(!file_exists($this->filepath)){return mkdir($this->filepath); | |
} | |
} | |
} |
调用上传类
$tmpName = $_FILES['file']['tmp_name']; | |
$blobNum = $_POST['blob_num']; | |
$totalBlobNum = $_POST['total_blob_num']; | |
$fileName = $_POST['file_name']; | |
$upload = new Upload($tmpName, $blobNum, $totalBlobNum, $fileName); | |
$data = $upload->uploadReturn(); | |
header('Content-type: application/json'); | |
return json_encode($data); |
依据如上步骤就能够实现将文件分成若干块进行上传性能
正文完