问题
-
虚拟主机上传文件大小限制100m,
-
有时会遇到非常大的文件上传,上传过程中耗时非常久,
-
可能服务器的限制设置了上传文件尺寸,返回“413 request entity too large”
整体逻辑
-
前端:上传文件时,进行文件分片;发起请求时,带上第几次分片上传、总片数。
-
后端:按照分片进行文件保存,当上传完最后一片数据时,进行文件合并,并删除分片文
示例代码 下载: http://downinfo.myhostadmin.net/upload.zip
只是演示功能,生产环境需要加强上传过滤
前端 upload.html
<html> <head> <meta charset="utf-8"> <title>分片上传</title> <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> </head> <body> <form method="post" id="myForm" enctype="multipart/form-data"><input type="file" id="file" name="file"><input type="submit" id="submit" value="上传"> </form><script type="text/javascript">$('#submit').on('click', function(e) {// 阻止默认表单提交e.preventDefault();// 获取属性var myfile = $('#file')[0].files[0];var ext = myfile.name.split('.').pop();// 判断ext是否为视频var is_video = ['mp4', 'avi', 'rmvb', 'mkv'].indexOf(ext) > -1;var fileId = getFileIdentifier(myfile);// 数据切片var chunks = fileSlice(myfile, is_video);console.log(chunks)// 发送分割数据段sendChunk(fileId, chunks);})function getFileIdentifier(file){// 获取文件标识符return file.name//return file.size + file.name;}function fileSlice(file, is_video) {// 切片不宜过大,过大需要 nginx 以及 php 做相应配置var chunkSize = 1024 * 1024 * 4; //切片大小控制// 1.初始化数据var totalSize = file.size;var start = 0;var end = start + chunkSize;var chunks = [];// 2.使用bolb提供的slice方法切片while (start < totalSize) {if (is_video) {console.log('视频')var chunk = file.slice(start, end, 'video/mp4');} else {console.log('图片')var chunk = file.slice(start, end);}chunks.push(chunk);start = end;end += chunkSize;}// 3.返回切片组chunk[]return chunks;}function sendChunk(id, chunks){// 逐个提交// 用于保证ajax发送完毕var task = [];var totalPage=0;var i=0;totalPage=chunks.length-1;var fileExt = id.substr(id.lastIndexOf('.') + 1);chunks.forEach(function(chunk, index){var formData = new FormData();formData.append('file',chunk);formData.append("fileName",id);formData.append("totalPage",totalPage);formData.append("page",index);$.ajax({type: "POST",url: 'upload.php',data: formData,contentType: false,processData: false,dataType:"json",async:false,success: function(data){// 移除已完成任务task.pop();if (data['status']==200){console.log(data['downUrl']);alert(data['downUrl']); //返回上传文件路径}}})task.push('file Working');})} </script> </body> </html>
后端 upload.php
<?php if (empty($_POST)) {$res = ['status' => 500];echo json_encode($res);exit; } // 创建上传目录 if(!is_dir('upload')){mkdir('upload', 0777);} // 创建上传缓存目录 if(!is_dir('tmp')){mkdir('upload', 0777);}$fileName = isset($_POST['fileName'])?$_POST['fileName']:''; $page = isset($_POST['page'])?$_POST['page']:''; $totalPage = isset($_POST['totalPage'])?$_POST['totalPage']:'';$fileTmpName = isset($_FILES['file'])?$_FILES['file']['tmp_name']:'';$status = 206; $downUrl = '';if ($fileName== ''|| $page == '' || $totalPage == '' || $fileTmpName == '') {$res = ['status' => 500];echo json_encode($res);exit(); }// 上传文件要保存的路径 $fname = sprintf('./tmp/%s-%s', $fileName, $page); $data = file_get_contents($fileTmpName); file_put_contents($fname, $data);// 整合分片文件 //if ($save) { if ($totalPage ==$page) {$uploadFileName = sprintf('./upload/%s%s', time(),$fileName); $status = 200;// 合并文件,删除分片文件 for ($i = 0; $i<=$totalPage; $i++) {$tmp = sprintf('./tmp/%s-%s', $fileName, $i);$data = file_get_contents($tmp);file_put_contents($uploadFileName, $data, FILE_APPEND);@unlink($tmp);}$dir = trim(dirname($_SERVER['PHP_SELF']), '/');if ($dir!='') {$dir .= '/';}$downUrl = sprintf('%s://%s/%s%s', $_SERVER['REQUEST_SCHEME'], $_SERVER['HTTP_HOST'], $dir,trim($uploadFileName, './'));$res = ['status' => $status,'downUrl' => $downUrl];echo json_encode($res);exit(); }// 返回上传状态 $res = ['status' => $status,'downUrl' => $downUrl]; echo json_encode($res);