php h5 实现分片上传
前端
<meta charset="utf-8" /> <style> body{ font-family:Arial; } form{ margin:50pxauto; width:600px; } input[type="button"]{ cursor:pointer; } table{ display:none; margin-top:15px; border:1pxsolid#ddd; border-collapse:collapse; } table th{ color:#666; } table td, table th{ padding:5px; border:1pxsolid#ddd; text-align:center; font-size:14px; } </style> <form method="post" id="myForm" action="./fileTest.php" enctype="multipart/form-data"> <input type="file" id="myFile" multiple=""> <!-- 上传的文件列表 --> <table id="upload-list"> <thead> <tr> <th width="35%">文件名</th> <th width="15%">文件类型</th> <th width="15%">文件大小</th> <th width="20%">上传进度</th> <th width="15%"> <input type="button" id="upload-all-btn" value="全部上传"> </th> </tr> </thead> <tbody></tbody> </table> </form> <script src="./js/jquery.min.js" type="text/javascript"></script> <script> //全部上传操作 $('#upload-all-btn').live('click',function(){ //未选择文件 if(!$('#myFile').val()){ $('#myFile').focus(); } //模拟点击其他可上传的文件 else{ $('#upload-list .upload-item-btn').each(function(){ $(this).click(); }); } }); // 选择文件-显示文件信息 var uploadItem=[]; $('#myFile').change(function(e){ var file, //uploadItemTpl=$('#file-upload-tpl').html(), uploadItemTpl = '<tr> <td width="35%">{{fileName}}</td><td width="15%">{{fileType}}</td><td width="15%">{{fileSize}}</td> <td width="20%" class="upload-progress">{{progress}}</td> <td width="15%"><input type="button" class="upload-item-btn" data-name="{{fileName}}" data-size="{{fileSize}}" data-state="default" value="{{uploadVal}}"></td> </tr>', size, percent, progress='未上传', uploadVal='开始上传'; for(var i=0,j=this.files.length;i<j;++i){ file=this.files[i]; percent=0; progress='未上传'; uploadVal='开始上传'; //计算文件大小 size=file.size>1024?file.size/1024>1024?file.size/(1024*1024)>1024?(file.size/ (1024*1024*1024)).toFixed(2)+'GB':(file.size/(1024*1024)).toFixed(2)+'MB':(file .size/1024).toFixed(2)+'KB':(file.size).toFixed(2)+'B'; //初始通过本地记录,判断该文件是否曾经上传过 percent=window.localStorage.getItem(file.name+'_p'); console.log(percent); if(percent&&percent!=='100.0'){ progress='已上传'+percent+'%'; uploadVal='继续上传'; } //更新文件信息列表 console.log(file); uploadItem.push(uploadItemTpl.replace(/{{fileName}}/g,file.name).replace('{{fileType}}',file.type||file.name.match(/\.\w+$/)+'文件').replace(/{{fileSize}}/g,size).replace('{{progress}}',progress).replace('{{totalSize}}',file.size).replace('{{uploadVal}}',uploadVal)); } $('#upload-list').children('tbody').html(uploadItem.join('')).end().show(); }); /** * 上传文件时,提取相应匹配的文件项 * @param {String} fileName 需要匹配的文件名 * @return {FileList} 匹配的文件项目 */ function findTheFile(fileName){ var files=$('#myFile')[0].files,theFile; for(var i=0,j=files.length;i<j;++i){ if(files[i].name===fileName){ theFile=files[i]; break; } } return theFile?theFile:[]; } //上传文件 $('.upload-item-btn').live('click',function(e){ var $this=$(this), state=$this.attr('data-state'), msg={ done:'上传成功', failed:'上传失败', in:'上传中...', paused:'暂停中...' }, fileName=$this.attr('data-name'), $progress=$this.closest('tr').find('.upload-progress'), eachSize=1024, totalSize=$this.attr('data-size'), chunks=Math.ceil(totalSize/eachSize), percent, chunk, //暂停上传操作 isPaused=0; //进行暂停上传操作 //未实现,这里通过动态的设置isPaused值并不能阻止下方ajax请求的调用 if(state==='uploading'){ $this.val('继续上传').attr('data-state','paused'); $progress.text(msg['paused']+percent+'%'); isPaused=1; console.log('暂停:',isPaused); } //进行开始/继续上传操作 else if(state==='paused' || state==='default'){ $this.val('暂停上传').attr('data-state','uploading'); isPaused=0; } //第一次点击上传 startUpload('first'); //上传操作 times: 第几次 function startUpload(times){ //上传之前查询是否以及上传过分片 chunk=window.localStorage.getItem(fileName+'_chunk')||0; chunk=parseInt(chunk,10); //判断是否为末分片 var isLastChunk=(chunk==(chunks-1)?1:0); //如果第一次上传就为末分片,即文件已经上传完成,则重新覆盖上传 if(times==='first'&&isLastChunk===1){ window.localStorage.setItem(fileName+'_chunk',0); chunk=0; isLastChunk=0; } //设置分片的开始结尾 var blobFrom=chunk*eachSize,//分段开始 blobTo=(chunk+1)*eachSize>totalSize?totalSize:(chunk+1)*eachSize,//分段结尾 percent=(100*blobTo/totalSize).toFixed(1),//已上传的百分比 timeout=5000,//超时时间 fd=new FormData($('#myForm')[0]); fd.append('theFile',findTheFile(fileName).slice(blobFrom,blobTo));//分好段的文件 //fd.append('theFile',123); fd.append('fileName',fileName);//文件名 fd.append('totalSize',totalSize);//文件总大小 fd.append('isLastChunk',isLastChunk);//是否为末段 fd.append('isFirstUpload',times==='first'?1:0);//是否是第一段(第一次上传) var xhr = new XMLHttpRequest(); xhr.open('POST', 'fileTest.php', true); xhr.upload.onprogress = function(e) { if (e.lengthComputable) { var percentComplete = (e.loaded / e.total) * 100; console.log(percentComplete + '% uploaded'); } }; xhr.onload = function() { rs=JSON.parse(this.response); //上传成功 if(rs.status===200){ //记录已经上传的百分比 window.localStorage.setItem(fileName+'_p',percent); //已经上传完毕 if(chunk===(chunks-1)){ $progress.text(msg['done']); $this.val('已经上传').prop('disabled',true).css('cursor','not-allowed'); if(!$('#upload-list').find('.upload-item-btn:not(:disabled)').length){ $('#upload-all-btn').val('已经上传').prop('disabled',true).css('cursor', 'not-allowed'); } }else{ //记录已经上传的分片 window.localStorage.setItem(fileName+'_chunk',++chunk); $progress.text(msg['in']+percent+'%'); //这样设置可以暂停,但点击后动态的设置就暂停不了.. // if (chunk == 10) { // isPaused = 1; // } console.log(isPaused); if(!isPaused){ startUpload(); } } } //上传失败,上传失败分很多种情况,具体按实际来设置 else if(rs.status===500){ $progress.text(msg['failed']); } /** if (this.status == 200) { var resp = JSON.parse(this.response); console.log('Server got:', resp); var image = document.createElement('img'); image.src = resp.dataUrl; document.body.appendChild(image); }; **/ }; xhr.send(fd); /* fd=new FormData($('#myForm')[0]); fd.append('theFile',findTheFile(fileName).slice(blobFrom,blobTo));//分好段的文件 //fd.append('theFile',123); fd.append('fileName',fileName);//文件名 fd.append('totalSize',totalSize);//文件总大小 fd.append('isLastChunk',isLastChunk);//是否为末段 fd.append('isFirstUpload',times==='first'?1:0);//是否是第一段(第一次上传) */ //上传 /* $.ajax({ type:'post', url:'./fileTest.php', data:fd, processData:false, contentType:false, timeout:timeout, success:function(rs){ rs=JSON.parse(rs); //上传成功 if(rs.status===200){ //记录已经上传的百分比 window.localStorage.setItem(fileName+'_p',percent); //已经上传完毕 if(chunk===(chunks-1)){ $progress.text(msg['done']); $this.val('已经上传').prop('disabled',true).css('cursor','not-allowed'); if(!$('#upload-list').find('.upload-item-btn:not(:disabled)').length){ $('#upload-all-btn').val('已经上传').prop('disabled',true).css('cursor', 'not-allowed'); } }else{ //记录已经上传的分片 window.localStorage.setItem(fileName+'_chunk',++chunk); $progress.text(msg['in']+percent+'%'); //这样设置可以暂停,但点击后动态的设置就暂停不了.. // if (chunk == 10) { // isPaused = 1; // } console.log(isPaused); if(!isPaused){ startUpload(); } } } //上传失败,上传失败分很多种情况,具体按实际来设置 else if(rs.status===500){ $progress.text(msg['failed']); } }, error:function(){ $progress.text(msg['failed']); } });*/ } }); </script>
后台
fileTest.php
<?php
header('Content-type:text/plain; charset=utf-8');
/*
print_r($_FILES);
print_r($_REQUEST);
*/
$files=$_FILES['theFile'];
$fileName=iconv('utf-8','gbk',$_REQUEST['fileName']);
$totalSize=$_REQUEST['totalSize'];
$isLastChunk=$_REQUEST['isLastChunk'];
$isFirstUpload=$_REQUEST['isFirstUpload'];
if($_FILES['theFile']['error']>0){
$status=500;
}else{
//此处为一般的文件上传操作
/**
if (!move_uploaded_file($_FILES['theFile']['tmp_name'], 'upload/'. $_FILES['theFile']['name'])) {
//
$status = 501;
//
} else {
//
$status = 200;
//
}**/
//以下部分为文件断点续传操作
//如果第一次上传的时候,该文件已经存在,则删除文件重新上传
if($isFirstUpload=='1'&&file_exists('upload/'.$fileName)&&filesize('upload/'.$fileName)==$totalSize){
unlink('upload/'.$fileName);
}
//否则继续追加文件数据
if(!file_put_contents('upload/'.$fileName,file_get_contents($_FILES['theFile']['tmp_name']),FILE_APPEND)){
$status=501;
}else{
//在上传的最后片段时,检测文件是否完整(大小是否一致)
if($isLastChunk==='1'){
if(filesize('upload/'.$fileName)==$totalSize){
$status=200;
}else{
$status=502;
}
}else{
$status=200;
}
}
}
echo json_encode(array(
'status'=>$status,
'totalSize'=>filesize('upload/'.$fileName),
'isLastChunk'=>$isLastChunk
));
?>
浙公网安备 33010602011771号