php 大文件分片上传,结合layui上传插件
原理是将文件切成小文件,ajax递归上传,然后在后台再拼接成一个整的文件
-
html 页面
<div class="layui-upload"> <input type="file" style="display: none;" id="fileElem" ></input> <button type="button" class="btn btn-primary radius layui-btn-normal" id="fileSelect">选择文件</button> <span style="color: #FF5722">每次只能上传1个文件,文件大小“小于等于150M”</span> <div class="layui-upload-list"> <table class="layui-table"> <thead> <tr style="width: 15%"><th>文件名</th> <th style="width: 10%">大小</th> <th style="width: 65%">进度</th> <th style="width: 10%">状态</th> </thead> <tbody id="demoList"></tbody> </table> </div> <button type="button" class="btn btn-success radius" id="upstart">开始上传</button> </div>
-
jq代码(js jq 都用上了,哈哈)
var fileSelect = document.getElementById("fileSelect"), fileElem = document.getElementById("fileElem"); fileSelect.addEventListener("click", function (e) { if (fileElem) { fileElem.click(); fileElem.addEventListener("change", handleFiles, false); } e.preventDefault(); // prevent navigation to "#" }, false); var fileList = ""; function handleFiles(){ fileList = this.files; var filecontent = ""; for( var i = 0 ; i < fileList.length ; i++ ){ // 定义允许上传文件的大小 var filesize = (fileList[i].size/1024/1024).toFixed(1); if(filesize >= file_size){ layer.msg('* 文件大小超出限制',{icon:2,time:3000}); fileList = ""; return false; } filecontent += '<tr>'+ '<td>'+ fileList[i].name +'</td>'+ '<td>'+ (fileList[i].size/1024/1024).toFixed(1) +'MB</td>'+ '<td>'+ '<div class="layui-progress" lay-filter="'+fileList[i].lastModified+'_'+i+'" lay-showPercent="true" lay-filter="progress_'+i+'">'+ '<div class="layui-progress-bar" lay-percent="0%"></div>'+ '</div>'+ '</td>'+ '<td id="'+fileList[i].lastModified+'_'+i+'">等待上传</td>'+ '<td style="display:none" id="filepath"></td>'+ '</tr>'; } $("#demoList").append(filecontent); } //--------------- var upstartBtn = document.getElementById('upstart'); //--------------- const LENGTH = 512 * 1024 * 1; var start = 0; var end = start + LENGTH; var blob; var blob_num = 1; var is_stop = 0 var file = null; var loading; //----------------------------- var upload_instance = new Upload(); var fileid = 0; upstartBtn.onclick = function () { for( var i = 0 ; i < fileList.length ; i++ ){ fileid = fileList[i].lastModified+'_'+i; upload_instance.addFileAndSend(fileList[i]); } return false; } function Upload() { //对外方法,传入文件对象 this.addFileAndSend = function (that) { // loading loading = layer.load(1, {shade: [0.3,'#fff']}); file = that; blob = cutFile(file); sendData(blob, file); blob_num += 1; } //切割文件 function cutFile(file) { var file_blob = file.slice(start, end); start = end; end = start + LENGTH; return file_blob; }; //发送文件 function sendData(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); console.log(blob_num); console.log(total_blob_num); $.ajax({ url:upload_url, type:'post', async:true, dataType:"json", data:form_data, contentType:false, processData:false, success:function(data){ console.log(data); if(data['code'] == 1){ // 进度条和状态设置 if (total_blob_num == 1) { progress = '100%'; } else { progress = (Math.min(100, (blob_num / total_blob_num) * 100)).toFixed(2) + '%'; } // 进度条 layui.element.progress(fileid, progress); // 状态 var type = document.getElementById(fileid); type.innerText = progress; console.log('progress-----' + progress); //继续上传 if (start < file.size && is_stop === 0) { blob = cutFile(file); sendData(blob, file); blob_num += 1; } //每次清空 form_data = ""; }else if(data['code'] == 2){ // 进度条 progress = '100%'; layui.element.progress(fileid, progress); // loading end layer.close(loading); // 上传成功 var type = document.getElementById(fileid); type.innerText = "上传成功"; var text = '<span class="filePath" style="display:none">'+data.file_path+'</span><span class="fileExtension" style="display:none">'+data.file_extension+'</span><span class="fileName" style="display:none">'+data.file_name+'</span>'; $("#filepath").append(text); //每次清空 form_data = ""; } },error:function(data){ // loading end layer.close(loading); layer.msg('* 未知错误',{icon:2,time:2000}); return; } }) } }
-
php 后台代码,我用的是tp5.0
-
upload.php 封装了一个上传,拼接的方法
<?php namespace app\admin\controller; use think\Controller; ini_set('memory_limit',-1); //要有足够大的内存来允许处理上传的文件 set_time_limit(0); //防止超时 class Upload extends Controller { private $filepath; //上传目录 private $tmpPath; //PHP文件临时目录 private $blobNum; //第几个文件块 private $totalBlobNum; //文件块总数 private $fileName; //文件名 public function __construct($tmpPath,$blobNum,$totalBlobNum,$fileName,$filepath){ $this->tmpPath = $tmpPath; $this->blobNum = $blobNum; $this->totalBlobNum = $totalBlobNum; $this->fileName = $fileName; $this->filepath = $filepath; $this->moveFile(); $this->fileMerge(); } private function fileMerge(){ if($this->blobNum == $this->totalBlobNum){ // 此处文件不应该拼接,应该是追加入内 // 实测修改后php占用很少的内存也可以实现大文件的上传和拼接操作 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); } //API返回数据 public function apiReturn(){ if($this->blobNum == $this->totalBlobNum){ if(file_exists($this->filepath.'/'. $this->fileName)){ $data['code'] = 2; $data['msg'] = 'success'; $data['file_path'] = $this->fileName; $data['file_name'] = substr($this->fileName,0,strrpos($this->fileName, '.')); $data['file_extension'] = substr(strrchr($this->fileName, '.'), 1); } }else{ if(file_exists($this->filepath.'/'. $this->fileName.'__'.$this->blobNum)){ $data['code'] = 1; $data['msg'] = 'waiting'; $data['file_path'] = ''; } } header('Content-type: application/json'); echo json_encode($data); } private function touchDir(){ if(!file_exists($this->filepath)){ return mkdir($this->filepath); } } }
-
-
控制器调用上传方法
// 上传文件 public function uploadhandle(){ // 定义存储路径 $uploadDir = ROOT_PATH . 'public' . DS . 'assets' . DS . 'zip'; $upload = new Upload($_FILES['file']['tmp_name'],$_POST['blob_num'],$_POST['total_blob_num'],$_POST['file_name'],$uploadDir); $upload->apiReturn(); }