基于php+webuploader的大文件分片上传,支持断点续传,带进度条
基于php+webuploader的大文件分片上传,带进度条,支持断点续传(刷新、关闭页面、重新上传、网络中断等情况)。文件上传前先检测该文件是否已上传,如果已上传提示“文件已存在”,如果未上传则直接上传。视频上传时会根据设定的参数(分片大小、分片数量)进行上传,上传过程中会在目标文件夹中生成一个临时文件夹,用于存储临时分片,等所有分片上传完毕后,会根据序号重新组合成一个完整的视频,临时文件被删除。
如果文件上传至七牛云,可参看基于php大文件分片上传至七牛云,带进度条
首先下载webuploader
效果图:
临时文件,用于存储分片
html代码
<title>webuploader分片上传</title> <meta charset="utf8"> <!--引入CSS--> <link rel="stylesheet" type="text/css" href="/static/webupload/webuploader.css"> <script type="text/javascript" src="/static/index/js/jquery.js"></script> <script type="text/javascript" src="/static/index/js/jquery.md5.js"></script> <!--引入JS--> <script type="text/javascript" src="/static/webupload/webuploader.js"></script> <div id="uploader" class="wu-example"> <!--用来存放文件信息--> <div id="thelist" class="uploader-list"></div> <div class="btns"> <div id="picker">选择文件</div> <button id="ctlBtn" class="btn btn-default">开始上传</button> </div> </div> <style> .progress{ height: 20px; width: 300px; background: #ccc; } .progress-bar{ height: 20px; background: #0a3536;} </style> <script> var uploadswf = "/static/webupload/Uploader.swf"; var chunkSize = 2*1024*1024; var server_url="uploadVedio"; var GUID = WebUploader.Base.guid();//一个GUID var chunkObj = {}; //用来记录文件的状态、上传中断的位置 var seq=1; var msg=""; $(function () { var $ = jQuery; var $list = $("#thelist"); WebUploader.Uploader.register({ "before-send-file":"beforeSendFile", "before-send": "beforeSend" }, { "beforeSendFile": function (file) { //上传前校验文件是否已经上传过 var deferred = WebUploader.Deferred(); $.ajax({ type:"POST", //上传前校验文件上传到第几片 url: "checkFile", data: { seq: seq, fileMd5: $.md5(file.name + file.size + file.ext), fileName:file.name }, dataType: "json", success: function (data) { console.log(data); chunkObj = data; chunkObj.type = data.type; chunkObj.chunk == data.chunk; msg = data.msg; if(data.type==404){ deferred.reject(); $("#" + file.id).find(".state").text(data.msg); }else if (data.type == 0) { deferred.reject(); $("#" + file.id).find(".state").text("文件已上传"); } else if (data.type == 1) { if (data.chunk) { deferred.resolve(); } } else { deferred.resolve(); } }, error: function () { deferred.resolve(); } }) //deferred.resolve(); return deferred.promise(); }, "beforeSend": function (block) { var deferred = WebUploader.Deferred(); var curChunk = block.chunk; var totalChunk = block.chunks; console.log(chunkObj) if (chunkObj.type == "1") { if (curChunk < chunkObj.chunk) { deferred.reject(); } else { deferred.resolve(); } } else { deferred.resolve(); } return deferred.promise(); } }); var uploader = WebUploader.create({ // 选完文件后,是否自动上传。 auto: false, // swf文件路径 swf: uploadswf, // 文件接收服务端。 server: server_url, // 内部根据当前运行是创建,可能是input元素,也可能是flash. pick: "#picker", chunked: true,//开始分片上传 chunkSize:1 * 1024 * 1024,//每一片的大小 threads: 1, formData: { }, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! resize: false }); // 当有文件被添加进队列的时候 uploader.on("fileQueued", function (file) { $list.append("<div id="" + file.id + "" class="item">" + "<div class="item-file"><div class="fileType-logo"></div>" + "<div class="fileMes"><h4 class="info">" + file.name + "</h4>" + "<p class="state">等待上传...</p>" + "</div></div></div>"); }); // 文件上传过程中创建进度条实时显示。 uploader.on("uploadProgress", function (file, percentage) { var $li = $("#" + file.id), $percent = $li.find(".progress .progress-bar"); // 避免重复创建 if (!$percent.length) { $percent = $("<div class="progress progress-striped active">" + "<div class="progress-bar" role="progressbar" style="width: 0%">" + "</div>" + "</div>").appendTo($li).find(".progress-bar"); } $li.find("p.state").text("上传中"); $percent.css("width", percentage * 100 + "%"); }); uploader.on("uploadBeforeSend", function (obj, data, headers) { var file = obj.cuted.file; data.test = 1; data.fileMd5 = $.md5(file.name + file.size + file.ext); }) // 文件上传成功,给item添加成功class, 用样式标记上传成功。 uploader.on("uploadSuccess", function (file, response) { if(response.status==299){ $("#" + file.id).find("p.state").text("文件已存在"); }else{ $("#" + file.id).find("p.state").text("已上传"); } }); // 文件上传失败,显示上传出错。 uploader.on("uploadError", function (file) { $("#" + file.id).find("p.state").text(msg); }); // 完成上传完了,成功或者失败,先删除进度条。 uploader.on("uploadComplete", function (file) { $("#" + file.id).find(".progress").fadeOut(); }); //所有文件上传完毕 uploader.on("uploadFinished", function () { //提交表单 }); //开始上传 $("#ctlBtn").click(function () { uploader.upload(); }); }); </script>
php请求后端
use appindexcontrollerUpload;
public function uploadVedio() { $model =new Upload(); $res = $model->doUpload(); $model->ajaxReturn($res); }
封装上传类
<?php namespace appindexcontroller; use thinkController; /** * 大文件分片上传 */ class Upload extends Controller { private $filepath = "uploads/"; //上传目录 private $blobNum; //第几个文件块 private $totalBlobNum; //文件块总数 private $fileName; //文件名 #允许上传的文件 private $allowExtension = ["mp4","avi","wmv"]; #文件后缀 private $fileExtension =""; #当前块内容 private $nowFile = ""; #文件大小 private $totalSize = 0; #文件总大小只允许1G private $allowFileSize = 0; #文件md5 前端传过来的 用于创建临时文件夹 上传完后删除 private $fileMd5=""; public function __construct($savePath =""){ $postData = $_POST; #测试断点上传 if(isset($postData["test"])){ sleep(1); } if($savePath){ $this->filepath = $this->filepath.$savePath; } # #文件名称 # var_dump($postData); $postData["name"] =isset($postData["name"])?$postData["name"]:""; $this->fileName =$postData["name"]; if($this->isHaveFile()){ $this->ajaxReturn(["status"=>299,"msg"=>"文件已存在!"]); } $this->fileMd5 =$postData["fileMd5"]; #允许文件的大小 1G $this->allowFileSize =(1*1024*1024*1024); if((int)$postData["size"]>$this->allowFileSize){ $this->ajaxReturn(["status"=>204,"msg"=>"文件大小超1G限制!"]); } #文件大小 $this->totalSize=$postData["size"]; $postData["chunks"]=isset($postData["chunks"])?(int)$postData["chunks"]:1; $postData["chunk"]=isset($postData["chunk"])?(int)$postData["chunk"]:0; if(!(int)$postData["chunks"]){ $this->ajaxReturn(["status"=>208,"msg"=>"chunks参数错误"]); } #当前块 $this->blobNum =$postData["chunk"]+1; #总共块 $this->totalBlobNum =$postData["chunks"]; #获取后缀 $fileExtension =explode(".",basename( $this->fileName)); $this->fileExtension=array_pop($fileExtension); #检测后缀是否在允许范围 $this->checkFileExtension(); $this->nowFile = $_FILES["file"]; if( $this->nowFile["error"] > 0) { $msg["status"] = 502; $msg["msg"] = "文件错误!"; $this->ajaxReturn($msg); } } public function doUpload(){ #临时文件移动到指定目录下 $res = $this->moveFile(); if($res["status"]==999){ return $this->fileMerge(); }else{ return $res; } } #创建md5 文件名 public function createFileName(){ return $this->filepath.$this->fileName; } #检测文件是否重复 public function isHaveFile(){ if(file_exists($this->filepath.$this->fileName)){ return true; } return false; } #文件合并 public function fileMerge(){ if ($this->blobNum == $this->totalBlobNum) { $fileName = $this->createFileName(); @unlink($fileName); #删除旧文件 #文件合并 文件名以 $handle=fopen($fileName,"a+"); for($i=1; $i<= $this->totalBlobNum; $i++){ #当前分片数 $this->blobNum = $i; #吧每个块的文件追加到 上传的文件中 fwrite($handle,file_get_contents($this->createBlockFileName())); } fclose($handle); #删除分片 for($i=1; $i<= $this->totalBlobNum; $i++){ $this->blobNum = $i; @unlink($this->createBlockFileName()); } #删除临时目录 @rmdir($this->filepath.$this->fileMd5); if(filesize($fileName) == $this->totalSize){ $msg["status"] = 200; $msg["msg"] = "上传成功!"; $msg["size"] = $this->totalSize; $msg["filename"] = "http://".$_SERVER["HTTP_HOST"]."/".$this->createFileName(); $msg["name"] = $this->fileName; }else{ $msg["status"] = 501; $msg["msg"] = "上传文件大小和总大小有误!"; @unlink($this->createFileName()); } return $msg; # $this->ajaxReturn($msg); } } #检测上传类型 public function checkFileExtension(){ if(!in_array(strtolower($this->fileExtension),$this->allowExtension)){ $this->ajaxReturn(["status"=>203,"msg"=>"文件类型不允许"]); } } #将临时文件移动到指定目录 public function moveFile(){ try{ #每个块的文件名 以文件名的MD5作为命名 $filename=$this->createBlockFileName(); #分片文件写入 $handle=fopen($filename,"w+"); fwrite($handle,file_get_contents($this->nowFile ["tmp_name"])); fclose($handle); #不是最后一块就返回当前信息 是最后一块往下执行合并操作 if($this->blobNum != $this->totalBlobNum) { $msg["status"] = 201; $msg["msg"] = "上传成功!"; $msg["blobNum"] = $this->blobNum; return $msg; #$this->ajaxReturn($msg); }else{ $msg["status"] = 999; $msg["msg"] = "上传成功!"; $msg["blobNum"] = $this->blobNum; return $msg; } }catch (Exception $e){ $msg["status"] = 501; $msg["error"] = $e->getMessage(); return $msg; #$this->ajaxReturn($msg); } } #创建分片文件名 public function createBlockFileName(){ $dirName = $this->filepath.$this->fileMd5."/"; if (!is_dir($dirName) ) { @mkdir($dirName, 0700); }; return $dirName.$this->blobNum.".part"; } #json格式放回处理 public function ajaxReturn($msg){ exit(json_encode($msg)); } }
——现在的努力,只为小时候吹过的牛逼! ——
原文地址:https://www.cnblogs.com/zxf100/archive/2023/02/03/17089739.html