JS - 文件上传组件WebUploader使用详解3(大文件的分片、并发上传)
作者:hangge | 2019-01-16 08:10
对于一些大文件,由于服务端请求大小的限制,如果直接上传可能无法上传成功。即使不限制大小也可能因为传输时间过长而超时。
(2)服务端代码(upload.php)这边复杂些:
所以要上传大文件,最好的方式就是使用分段上传。即将原始文件分成一片片单独上传(每个分片大小建议 5M 左右),服务端这边在所有分片都接收完毕后再将它们合并起来。
1,效果图
(1)这里我上传一个 17M 的文件(开启分片上传,单片 4M),客户端这边表面上看不出什么不同。
(2)但打开控制台就可以看到,这个文件实际分成了 5 个请求并发提交。
2,样例代码
(1)客户端代码(index.html)这边改动不大,我们只需设置好“开启分片上传”、“每个分片的大小”这两个配置项即可,客户端会自动判断是否需要分片:
- 如果文件需要分片上传的话:在每个分片文件上传的同时,还会自动带上 chunks 和 chunk 两个参数,它们分别代表分片数和当前分片索引。
- 如果文件不需要分片的话:逻辑和不开启分片一样(也没有 chunks 和 chunk 两个参数)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <!--引入CSS--> <link rel="stylesheet" type="text/css" href="webuploader/webuploader.css"> <!--引入JS--> <script type="text/javascript" src="js/jquery-1.11.1.js"></script> <script type="text/javascript" src="webuploader/webuploader.js"></script> <script type="text/javascript"> $(function() { //开始上传按钮 var $btn = $('#ctlBtn'); //文件信息显示区域 var $list = $('#thelist'); //当前状态 var state = 'pending'; //初始化Web Uploader var uploader = WebUploader.create({ // swf文件路径 swf: 'webuploader/Uploader.swf', // 文件接收服务端。 server: 'http://www.hangge.com/upload.php', // 选择文件的按钮。可选。 // 内部根据当前运行是创建,可能是input元素,也可能是flash. pick: '#picker', // 开启分片上传。 chunked: true, //每个分片的大小(这里设为4M) chunkSize:4*1024*1024 }); // 当有文件被添加进队列的时候(选择文件后调用) uploader.on( 'fileQueued', function( file ) { $list.append( '<div id="' + file.id + '" class="item">' + '<h4 class="info">' + file.name + '</h4>' + '<p class="state">等待上传...</p>' + '</div>' ); }); // 文件上传过程中创建进度条实时显示。 uploader.on( 'uploadProgress', function( file, percentage ) { var $li = $( '#'+file.id ); $li.find('p.state').text('上传中(' + parseInt(percentage * 100) + '%)'); }); // 文件上传成功后会调用 uploader.on( 'uploadSuccess', function( file ) { $( '#'+file.id ).find('p.state').text('已上传'); }); // 文件上传失败后会调用 uploader.on( 'uploadError', function( file ) { $( '#'+file.id ).find('p.state').text('上传出错'); }); // 文件上传完毕后会调用(不管成功还是失败) uploader.on( 'uploadComplete', function( file ) { $( '#'+file.id ).find('.progress').fadeOut(); }); // all事件(所有的事件触发都会响应到) uploader.on( 'all', function( type ) { if ( type === 'startUpload' ) { state = 'uploading'; } else if ( type === 'stopUpload' ) { state = 'paused'; } else if ( type === 'uploadFinished' ) { state = 'done'; } if ( state === 'uploading' ) { $btn.text('暂停上传'); } else { $btn.text('开始上传'); } }); // 开始上传按钮点击事件响应 $btn.on( 'click', function() { if ( state === 'uploading' ) { uploader.stop(); } else { uploader.upload(); } }); }); </script> <style> #picker { display: inline-block; } #ctlBtn { position: relative; display: inline-block; cursor: pointer; background: #EFEFEF; padding: 10px 15px; color: #2E2E2E; text-align: center; border-radius: 3px; overflow: hidden; } #ctlBtn:hover { background: #DDDDDD; } </style> </head> <body> <div id="uploader" class="wu-example"> <div class="btns"> <div id="picker">选择文件</div> <div id="ctlBtn" class="webuploader-upload">开始上传</div> </div> <!--用来存放文件信息--> <div id="thelist" class="uploader-list"></div> </div> </body> </html>
(2)服务端代码(upload.php)这边复杂些:
- 如果上传的文件没有分片,那么就直接保存(逻辑和以前一样)
- 如果上传的是单个分片,那么将分片保存到 tmp 文件夹下,命名为“文件名.tmp*”(* 号为该分片索引)
- 每次分片保存后会判断 tmp 文件夹下是否所有分片都已经有了,是的话则合并成完整文件,并删除 tmp 文件夹下的临时文件。
<?php //获取文件名 $name = $_POST["name"]; //获取大小 $size = $_POST["size"]; //获取文件类型 $type= $_POST["type"]; //获取文件最后修改时间 $lastModifiedDate= $_POST["lastModifiedDate"]; //获取分片总数 $chunks= $_POST["chunks"]; //获取当前分片索引 $chunk= $_POST["chunk"]; // 文件保存路径 $upload = $_SERVER["DOCUMENT_ROOT"]."/uploadFiles"; // 临时文件保存路径(分片) $tmp = $_SERVER["DOCUMENT_ROOT"]."/uploadFiles/tmp"; // 判断文件夹是否存在,不存在则创建 if (!is_dir($tmp)){ mkdir($tmp, 0777, true); } //如果不分片的话直接保存 if (!isset($chunks)) { //将上传的文件保存到指定目录下 move_uploaded_file($_FILES["file"]["tmp_name"], $upload."/".$name); //输出信息 echo "--- 文件上传完毕 ---\n"; echo "文件名:".$name."\n"; echo "文件大小:".$size."\n"; echo "文件类型:".$type."\n"; echo "文件最后修改时间:".$lastModifiedDate; } else { // 如果分片的话先把分片存储到tmp文件夹下 move_uploaded_file($_FILES["file"]["tmp_name"], $tmp."/".$name.".tmp".$chunk); echo "--- 分片上传完毕 ---\n"; // 判断所有分片是否都上传完毕了 $complete = true; for($i = 0; $i < $chunks; $i++) { if(!file_exists($tmp."/".$name.".tmp".$i)){ $complete = false; break; } } //如果所有分片都有的话就开始合并 if ($complete) { $fp = fopen($upload."/".$name, "ab"); for($i = 0; $i < $chunks; $i++) { $tmp_file = $tmp."/".$name.".tmp".$i; $handle = fopen($tmp_file, "rb"); fwrite($fp, fread($handle, filesize($tmp_file))); fclose($handle); unset($handle); unlink($tmp_file);//合并完毕的文件就删除 } echo "--- 文件合并完毕 ---\n"; } } ?>
全部评论(0)