index.html
<!DOCTYPE html>
<html>
<head>
<title>大文件上传</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="https://www.layuicdn.com/layui/css/layui.css" />
</head>
<body>
<div>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>大文件上传</legend>
</fieldset>
<input type="file" name="file" id="file_demo">
<div class="layui-upload-list">
<img id="demo1">
<p id="demoText"></p>
</div>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 50px;">
<legend>上传进度</legend>
</fieldset>
<div class="layui-progress layui-progress-big" lay-showpercent="true" lay-filter="demo">
<div class="layui-progress-bar layui-bg-red" lay-percent="0%"></div>
</div>
<button type="button" class="layui-btn" style="margin-top: 50px;" lay-active="begin">开始上传</button>
</div>
</body>
<script type="text/javascript" src="https://www.layuicdn.com/layui-v2.5.4/layui.js"></script>
<script type="text/javascript">
var BYTES_PER_SLICE = 2<<18;
console.log(BYTES_PER_SLICE);
layui.use(['element','util','layer'],function(){
var $ = layui.$
,element = layui.element
,util = layui.util
,layer = layui.layer
,BYTES_PER_SLICE = 2<<18 // 切片大小
,hasSendNum = 0 // 以发送数量
,totalSlices // 总切片数
,file,totalSize;
util.event('lay-active',{
begin: function() {
// 文件
file = $('#file_demo')[0].files[0]
if(file==undefined){
alert("文件为空")
window.location.reload()
}
// 文件的总字节数
totalSize = file.size
// 当前切片数
var index = 0
// 切片的开始和结束
,start,end
// 上传文件名
,fileName = file.name;
// 初始化以发送数量
hasSendNum = 0;
// 总切片数
totalSlices = Math.ceil(totalSize/BYTES_PER_SLICE);
checkFile(fileName)
//uploadSliceFile(index, fileName);
}
},'click')
function checkFile(fileName){
$.ajax({
url: './checkFile.php'
,data: {"file":fileName}
,type: 'post'
,async: false
,success: function(obj){
var arr = JSON.parse(obj)
console.log(arr)
if(arr.page>1){
uploadSliceFile(arr.page,fileName)
}else{
uploadSliceFile(arr.page,fileName)
}
}
})
}
function uploadSliceFile(page, filename){
// 切片开始位置
start = page * BYTES_PER_SLICE;
// 切片结束位置
end = start + BYTES_PER_SLICE
// 切割文件
var slice = file.slice(start,end);
console.log("----------")
console.log(start)
console.log(end)
console.log(filename)
var Data = new FormData();
Data.append('file', slice);
Data.append('page', page);
Data.append('filename', filename);
Data.append('pages', totalSlices)
$.ajax({
url: './index.php'
,data: Data
,type: 'post'
,dataType: 'json'
,processData : false // 使数据不做处理
,contentType : false // 不要设置Content-Type请求头
,async: true
,success: function(obj){
var progress = (((page + 1)/totalSlices) * 100).toFixed(2)
element.progress('demo',obj.code == 1 ? '100%' : progress+'%');
if(page + 1 < totalSlices){
uploadSliceFile(++page, filename);
}
}
,error: function(response){
// 失败时 重新上传
return false
}
})
}
})
</script>
</html>
index.php:
$post = !empty($_POST) ? $_POST : '';
$file = !empty($_FILES['file']) ? $_FILES['file'] : '';
if(!$post || !$file){
echo json_encode(['code' => 1001, 'msg' => 'error', 'data' => []]);exit;
}
$uploadSliceFile = new uploadSliceFile($post['filename'], $post['page'], $post['pages'], $file['tmp_name']);
$uploadSliceFile ->apiReturn();
class uploadSliceFile{
private $filename; // 文件名
private $filepath = "../file/uploads/"; // 分片上传目录
private $filespath = '../file/upload/'; // 文件上传目录
private $sliceNum; // 分块数(第几页
private $tmpPath; // 上传文件临时目录
private $totalBlobNum; // 总块数(总页数)
public function __construct($filename, $sliceNum, $totalBlobNum, $tmpPath){
$this ->filename = $filename;
$this ->sliceNum = $sliceNum;
$this ->totalBlobNum = $totalBlobNum;
$this ->tmpPath = $tmpPath;
$this ->filepath .= md5($this ->filename);
$this ->moveFile();
$this ->fileMerge();
}
//API返回数据
public function apiReturn(){
if($this->sliceNum == $this->totalBlobNum - 1){
if(file_exists($this->filespath.'/'. $this->filename)){
$data['code'] = 1;
$data['msg'] = 'success';
$data['file_path'] = $this->filespath.'/'. $this->filename;
}
}else{
if(file_exists($this->filepath.'/'. $this->filename.'_'.$this->sliceNum)){
$data['code'] = -1;
$data['msg'] = 'waiting';
$data['file_path'] = '';
}
}
// header('Content-type: application/json');
echo json_encode($data);
}
//移动文件
private function moveFile(){
$this->touchDir();
$filename = $this->filepath.'/'. $this->filename.'_'.$this->sliceNum;
move_uploaded_file($this->tmpPath,$filename);
}
//建立上传文件夹
private function touchDir(){
if(!file_exists($this->filepath)){
return mkdir($this->filepath);
}
}
//判断是否是最后一块,如果是则进行文件合成并且删除文件块
private function fileMerge(){
if($this->sliceNum == $this->totalBlobNum - 1){
$blob = '';
for($i = 0; $i < $this->totalBlobNum; $i++){
$blob = file_get_contents($this->filepath.'/'. $this->filename.'_'.$i);
if(!file_exists($this->filespath)){
mkdir($this->filepath,775,true);
}
file_put_contents($this->filespath.'/'. $this->filename,$blob,FILE_APPEND);
}
$this->deleteFileBlob();
}
}
//删除文件块
private function deleteFileBlob(){
for($i = 0; $i < $this->totalBlobNum; $i++){
unlink($this->filepath.'/'. $this->filename.'_'.$i);
@rmdir($this->filepath);
}
}
}
checkFile.php
$post = !empty($_POST['file']) ? $_POST : '';
$checkFile = new checkFile($post['file']);
class checkFile{
private $filename; // 文件名
private $filepath = "../file/uploads/"; // 分片上传目录
public function __construct($filename){
$this ->filename = $filename;
$this ->filepath .= md5($this ->filename);
// mkdir($this ->filepath,755,true);
$this ->checkDir();
}
public function checkDir(){
if(file_exists($this ->filepath)){
$i = 0;
while ($i<(2<<15)) {
if(!file_exists($filename = $this->filepath.'/'. $this->filename.'_'.$i)&&$i>0){
$data['code'] = 3;
$data['msg'] = 'success';
$data['page'] = $i;
echo json_encode($data);break;
}
$i++;
}
}else{
$data['code'] = -3;
$data['msg'] = 'success';
$data['page'] = 0;
echo json_encode($data);
}
}
}