为了解决大文件上传 (PHP上传最大限制2GB)
同时为了解决文件上传是对服务器造成的压力
可以通过分段上传解决这个问题,这得益于HTML5开发的file API
前台代码:
引用了进度条插件myProgress.js
<link href="__PUBLIC__/admin/css/myProgress.css" rel="stylesheet"> <script src="__PUBLIC__/admin/js/jquery.myProgress.js"></script> <div> <div> <form id="myForm"> <div> //上传文件时由用户指定文件名 <label for="FileName">File Name</label> <input type="text" name="title" class="form-control" id="FileName"> </div> <div> <label for="myFile">Chose File</label> <input type="file" id="myFile"> <div class="progress-out" id="progress"> <div class="percent-show"><span>0</span>%</div> <div class="progress-in"></div> </div> </div> </form> <button type="button" class="btn btn-default" id="btn">Submit</button> </div> </div> <script> //初始化上传 function initUpload() { var chunk = 1000*1024; //每片大小 var input = document.getElementById("myFile"); //input file input.onchange = function (e) { //获得上传的文件 var file = this.files[0]; //如果大于指定大小 提示错误 if(file.size > 1*1024*1024*1024){ $('#help_msg').removeClass('help-block').addClass('error-block'); return ; }else{ $('#help_msg').css('display','none'); } // 开启进度条 $("#progress").css('display','block'); $("#progress").myProgress({speed: 1000, percent: 0, "200px", height: "12px"}); var query = {}; var chunks = []; if (!!file) { var start = 0; //文件分片 for (var i = 0; i < Math.ceil(file.size / chunk); i++) { //最后一段取文件的真实大小 var end = 0; if(i == (Math.ceil(file.size / chunk)-1)){ end = file.size; }else{ end = start + chunk; } chunks[i] = file.slice(start , end); start = end; } // 采用post方法上传文件 // url query上拼接以下参数,用于记录上传偏移 // post body中存放本次要上传的二进制数据 query = { fileName : file.name, fileSize: file.size, dataSize: chunk, nextOffset: 0 } upload(chunks, query, successPerUpload); } } } // 执行上传 function upload(chunks, query, cb) { //对象转字符串 用&连接 var queryStr = Object.getOwnPropertyNames(query).map(key => { return key + "=" + query[key]; }).join("&"); var xhr = new XMLHttpRequest(); xhr.open("POST", "/Shop/index.php/Admin/File/upload_file?" + queryStr); xhr.overrideMimeType("application/octet-stream"); //获取post body中二进制数据 var index = Math.floor(query.nextOffset / query.dataSize); getFileBinary(chunks[index], function (binary) { if (xhr.sendAsBinary) { xhr.sendAsBinary(binary); } else { xhr.send(binary); } }); xhr.onreadystatechange = function (e) { if (xhr.readyState === 4) { if (xhr.status === 200) { var resp = JSON.parse(xhr.responseText); //通过返回数据更新进度条 var precent = Math.ceil((resp.offset / query.fileSize) * 100); $("#progress").myProgress({speed: 1000, percent: precent, "200px", height: "12px"}); // 接口返回nextoffset // resp = { // isFinish:false, // offset:100*1024 // } if (typeof cb === "function") { cb.call(this, resp, chunks, query) } } } } } // 每片上传成功后执行 function successPerUpload(resp, chunks, query) { if (resp.isFinish === true) { //上传完成给出提示 $('#help_msg').css('display','block').addClass('error-block').html('success!'); } else { //未上传完毕 query.nextOffset = resp.offset; upload(chunks, query, successPerUpload); } } // 获取文件二进制数据 function getFileBinary(file, cb) { var reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onload = function (e) { if (typeof cb === "function") { cb.call(this, this.result); } } } //初始化上传 initUpload(); //ajax模拟提交表单 $(function(){ $('#btn').click(function(){ var fd = new FormData(document.querySelector('#myForm')); var input = document.getElementById("myFile"); //input file var file = input.files[0]; if(!file){ $('#help_msg').css('display','block').addClass('error-block').html('please chose the file !'); return ; } fd.append('FileName',file.name); fd.append('size',file.size); $.ajax({ url : "/Shop/index.php/Admin/File/add", type: "POST", async : true, data: fd, processData: false, // 不处理数据 contentType: false, // 不设置内容类型 success : function(result){ console.log(result); if(result.res == 1){ window.location.href = "http://localhost:8080/Shop/index.php/Admin/File/index"; }else{ $("#help_msg_1").css('display','block').html('upload faild ' + $result.msg); } } }); }) }) </script>
后台PHP代码
public function add(){ if($_POST){ $Attach = D('Attachment'); $file_path = './Upload/File/'.$_POST['FileName']; //如果是win系统将文件名改成GBK编码 if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){ $file_path = iconv('UTF-8', 'GBK', $file_path); } if(file_exists($file_path)){ //获得拓展名 $ext = strtolower(trim(substr(strrchr($_POST['FileName'], '.'), 1))); //生成新的文件名 $url = './Upload/File/'.date("Ymdhms").rand(1000,9999).'.'.$ext; $_POST['url'] = $url ; //将上传的文件改名,将新的路径存入数据库 if(rename($file_path, $url)){ $res = $Attach -> add_file($_POST); if($res['res']){ $log['remark'] = session('userinfo')['name'].'在'.date("Y-m-d H:i:s").'上传了文件'; D('ActionLog') -> add_log($log); $this -> ajaxReturn(array('res' => 1)); }else{ $this -> ajaxReturn(array('res' => 0 , 'msg' => $res['msg'])); } } }else{ $this -> ajaxReturn(array('res' => 0, 'msg' => '上传文件不存在')); } }else{ $this -> show(); } } //异步分段上传文件 public function upload_file(){ $path = './Upload/File/'.$_GET['fileName']; if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){ $path = iconv('UTF-8', 'GBK', $path); } if(!file_exists($path)){ $handle = fopen($path, "a+"); fclose($handle); } file_put_contents($path, file_get_contents('php://input'),FILE_APPEND|LOCK_EX); $offset = filesize($path); if( $offset >= $_GET['fileSize'] ){ $this -> ajaxReturn(array('isFinish' => true)); }else{ $this -> ajaxReturn(array('isFinish' => false , 'offset' => $offset)); } }