zoukankan      html  css  js  c++  java
  • 大文件分片上传,断点续传,秒传 实现

    前段时间做视频上传业务,通过网页上传视频到服务器。
    视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制;2,请求时间过长,请求超时;3,传输中断,必须重新上传导致前功尽弃;
     
    解决方案:
    1,修改服务端上传的限制配置;Nginx 以及 PHP 的上传文件限制 不宜过大,一般5M 左右为好;
    2,大文件分片,一片一片的传到服务端,再由服务端合并。这么做的好处在于一旦上传失败只是损失一个分片而已,不用整个文件重传,而且每个分片的大小可以控制在4MB以内,服务端限制在4M即可。

    前端

    Web前端可使用百度的 webuploader H5大文件分片上传插件;官网地址:http://fex.baidu.com/webuploader/ 

    <div class="section section6 section5">
        <div class="part1"><a href="javascript:;" target="_blank" class="part1__btn">批量删除</a><span class="part1__txt"><em class="part1__num" id="upload_num">0</em>个视频,共 <em class="part1__num" id="upload_size">0M</em></span></div>
        <table class="section5__table">
            <tbody id="thelist">
                <tr class="thead">
                    <th class="col1 allCkeck"><input type="checkbox" name="" class="col1__checkBox"/>视频名称</th><th class="col2">视频大小</th><th class="col3">视频分类</th><th class="col4">状态</th><th class="col5">进度</th><th>操作</th>
                </tr>
            </tbody>
        </table>
        <div class="selFile" id="selFile">
            <div id="drag_tips">
                <div id="btns__add2"></div>
                <h2 class="txt1">选择视频文件</h2>
                <span class="txt2">或直接将文件拖拽至此窗口</span>
            </div>
        </div>
        <div class="btns"><span class="btns__add" id="btns__add">+添加视频文件</span><span class="btns__upload btns__upload-start" id="uploadBtn"><i class="btns__upload_icon"></i>开始上传视频</span></div>
    </div>
    //引入插件
    <
    script type="text/javascript" src="media/js/lib/webuploader/js/webuploader.min.js"></script>

    upload.js

      1 // 文件上传
      2 jQuery(function() {
      3     var $ = jQuery,
      4         $list = $('#thelist'),
      5         $btn = $('#upload-start'),
      6         $thead = $('.thead'),
      7         $part_btn = $('.part1__btn'), //批量上传按钮
      8         state = 'pending',
      9         fileCount = 0, //上传文件总数
     10         fileSize = 0,//上传文件的总大小
     11     // 上传按钮
     12         $upload = $('#uploadBtn'),
     13     // 所有文件的进度信息,key为file id
     14         percentages = {},
     15     // 所有文件的md5,key为file id
     16         md5Obj = {},
     17     // 可能有pedding, ready, uploading, confirm, done.
     18         state = 'pedding',
     19         uploader;
     20 
     21     //浏览器关闭提醒
     22     window.is_confirm = false;
     23     $(window).bind('beforeunload', function(){
     24         // 只有在标识变量is_confirm不为false时,才弹出确认提示
     25         if(window.is_confirm !== false)
     26             return '正在上传视频,该操作将丢失视频,是否继续?';
     27     })
     28 
     29     if ( !WebUploader.Uploader.support() ) {
     30         alert( 'Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级浏览器');
     31         throw new Error( 'WebUploader does not support the browser you are using.' );
     32     }
     33 
     34     $(".pop2 .btns__sure").click(function(){
     35         $('.popup,.pop').hide();
     36     });
     37 
     38     uploader = WebUploader.create({
     39         //拖拽容器
     40         dnd:'#selFile',
     41 
     42         // 不压缩image
     43         resize: false,
     44 
     45         // swf文件路径
     46         swf: '/media/js/lib/webuploader/js/Uploader.swf',
     47 
     48         // 文件接收服务端。
     49         server: '/service/upload/upload_file',
     50         //server:'http://vod.test.4399sy.com/service/upload/ssl_upload_file',
     51         formData: {
     52             file_id: 'file',
     53             guid:new Date().getTime() + Math.ceil(Math.random()*100),
     54             file_name:''
     55         },
     56 
     57         // 选择文件的按钮。可选。
     58         // 内部根据当前运行是创建,可能是input元素,也可能是flash.
     59         pick: {
     60             id:'#btns__add',
     61             innerHTML:"+添加视频文件"
     62         },
     63 
     64         // 开起分片上传。
     65         chunked: true,
     66 
     67         //如果要分片,分多大一片2M
     68         chunkSize:2*1024*1024,
     69 
     70         //上传文件的类型
     71         accept:{
     72             title: 'Videos',
     73             extensions: 'mp4,avi,flv',
     74             mimeTypes: 'video/*'
     75         },
     76         //验证文件总数量, 超出则不允许加入队列。
     77         fileNumLimit: 10,
     78         //单个文件上传的大小限制 2G
     79         fileSingleSizeLimit:2*1024*1024*1024,
     80 
     81     });
     82 
     83     //添加文件具体函数
     84     function addFile( file ){
     85         var data = new Date(),
     86             month = (data.getMonth()+1)<10 ? '0'+(data.getMonth()+1) : (data.getMonth()+1),
     87             day = data.getDate()<10 ? '0'+ data.getDate(): data.getDate(),
     88             time = data.getFullYear() + "-" + month + "-" + day,
     89             $tr = $('<tr class="toBeUploaded" id="'+file.id+'"></tr>'),
     90             $td = $('<td class="col1"><input type="checkbox" name="" class="col1__checkBox"/><input type="text" value="'+ file.name +'" name="" class="name"/></td><td class="col2">'+convert_size(file.size)+'</td><td class="col3"><select class="class_id">'+ class_options +'</select></td><td class="col4">读取视频中</td><td class="col5">0%</td><td class="col6"><ul><li class="view"><a target="_blank" href="javascript:;">查看</a></li><li class="delete">删除</li></ul></td>').appendTo($tr),
     91             $state = $tr.find('td.col4'),
     92             $prgress = $tr.find('td.col5'),
     93             $delbtn = $tr.find('li.delete');
     94 
     95         $("#selFile").hide();
     96 
     97         if ( file.getStatus() === 'invalid' ) {
     98             switch( file.statusText ) {
     99                 case 'exceed_size':
    100                     text = '文件大小超出';
    101                     break;
    102 
    103                 case 'interrupt':
    104                     text = '上传暂停';
    105                     break;
    106 
    107                 default:
    108                     text = '上传失败,请重试';
    109                     break;
    110             }
    111             showError(text);
    112         } else {
    113             // @todo lazyload
    114             percentages[ file.id ] = [ file.size, 0 ];
    115             file.rotation = 0;
    116         }
    117 
    118         file.on('statuschange', function( cur, prev ) {
    119             if ( prev === 'progress' ) {
    120                 //上传成功
    121             } else if ( prev === 'queued' ) {
    122                 // 开始上传
    123             }
    124 
    125             // 成功
    126             if ( cur === 'error' || cur === 'invalid' ) {
    127                 console.log( file.statusText );
    128                 showError( file.statusText );
    129                 percentages[ file.id ][ 1 ] = 1;
    130             } else if ( cur === 'interrupt' ) {
    131                 showError( 'interrupt' );
    132             } else if ( cur === 'queued' ) {
    133                 percentages[ file.id ][ 1 ] = 0;
    134             } else if ( cur === 'progress' ) {
    135             //   正在上传
    136 
    137             } else if ( cur === 'complete' ) {
    138             //   上传完成
    139 
    140             }
    141 
    142             $tr.removeClass( 'state-' + prev ).addClass( 'state-' + cur );
    143         });
    144         $delbtn.on('click',function(){
    145             uploader.removeFile( file );
    146         });
    147         $tr.appendTo($list);
    148         //$tr.insertAfter($thead);
    149     }
    150 
    151     // 负责view的销毁
    152     function removeFile( file ) {
    153         var $tr = $('#'+file.id);
    154 
    155         delete percentages[ file.id ];
    156         $tr.off().find('.col6').off().end().remove();
    157     }
    158 
    159     function setState( val ) {
    160         var file, stats;
    161 
    162         if ( val === state ) {
    163             return;
    164         }
    165 
    166         $upload.removeClass( 'state-' + state );
    167         $upload.addClass( 'state-' + val );
    168         state = val;
    169 
    170         switch ( state ) {
    171             case 'pedding':
    172                 uploader.refresh();
    173                 break;
    174 
    175             case 'ready':
    176                 uploader.refresh();
    177                 break;
    178 
    179             case 'uploading':
    180                 $upload.text( '暂停上传' );
    181                 break;
    182             case 'paused':
    183                 $upload.text( '继续上传' );
    184                 break;
    185 
    186             case 'confirm':
    187                 //$progress.hide();
    188                 $upload.text( '开始上传' ).addClass( 'disabled' );
    189 
    190                 stats = uploader.getStats();
    191                 if ( stats.successNum && !stats.uploadFailNum ) {
    192                     setState( 'finish' );
    193                     return;
    194                 }
    195                 break;
    196             case 'finish':
    197                 stats = uploader.getStats();
    198                 if ( stats.successNum ) {
    199                     alert( '上传成功' );
    200                 } else {
    201                     // 没有成功的图片,重设
    202                     state = 'done';
    203                     location.reload();
    204                 }
    205                 break;
    206         }
    207     }
    208 
    209 
    210     // 当有文件添加进来的时候
    211     uploader.on( 'fileQueued', function( file ) {
    212         fileCount++;
    213         fileSize += file.size;
    214         $("#upload_num").text(fileCount);
    215         $("#upload_size").text(convert_size(fileSize));
    216         md5Obj[ file.id ] = '';
    217         //获取文件MD5 值
    218         uploader.md5File( file )
    219             // 及时显示进度
    220             .progress(function(percentage) {
    221                 $( '#'+file.id ).find('.col4').text('读取文件'+parseInt(percentage*100)+"%");
    222             })
    223             // 完成
    224             .then(function(val) {
    225             console.log('md5 result:', val);
    226             md5Obj[ file.id ] = val;
    227             $( '#'+file.id ).find('.col4').text('待上传');
    228             setState( 'ready' );
    229         });
    230         addFile( file );
    231     });
    232 
    233     // 删除文件
    234     uploader.onFileDequeued = function( file ) {
    235         fileCount--;
    236         fileSize -= file.size;
    237         $("#upload_num").text(fileCount);
    238         $("#upload_size").text(convert_size(fileSize));
    239         if ( !fileCount ) {
    240             setState( 'pedding' );
    241         }
    242         removeFile( file );
    243 
    244     };
    245 
    246     // 添加“添加文件”的按钮,
    247     uploader.addButton({
    248         id: '#btns__add2',
    249         label: ''
    250     });
    251 
    252     // 文件上传过程中创建进度实时显示。
    253     uploader.onUploadProgress = function( file, percentage ) {
    254         var $tr = $('#'+file.id),
    255             $percent = $tr.find('td.col5'),
    256             $state = $tr.find('td.col4');
    257         percentage = parseInt(percentage*100);
    258         if(! (percentage == 0 && percentage == 100)){
    259             $state.text("正在上传");
    260         }
    261         $percent.text( percentage + "%")
    262         percentages[ file.id ][ 1 ] = percentage;
    263     };
    264 
    265     //上传前,请求服务端 判断文件是否已经上传过
    266     uploader.on( 'uploadStart', function( file ) {
    267         var type = 'POST';
    268         var url = '/service/upload/determine_video_exist';
    269         var request_data = {
    270             'md5': md5Obj[ file.id ],
    271             'type':1
    272         };
    273         var success = function(r) {
    274             uploader.upload( file );
    275             console.log(r);
    276             if(r.code == 1) {
    277                 uploader.skipFile( file );
    278                 $( '#'+file.id ).find('.col4').text('视频已存在');
    279                 $( '#'+file.id ).find('.col5').text('100%');
    280                 $('#'+file.id).find('.view').find('a').attr('href',playmain +'/?video_id='+ r.data.video_id);
    281                 $('.pop2 .video_game').text("所在游戏:"+r.data.game_name);
    282                 $('.pop2 .create_time').text("上传时间:"+r.data.create_time);
    283                 $('.pop').hide();
    284                 $('.pop2').show();
    285                 $('.popup').show();
    286             }else if(r.code <= 0) {
    287                 showError(r.msg);
    288             }else {
    289 
    290             }
    291         };
    292         request(type, url, request_data, success);
    293     });
    294 
    295     //当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。
    296     uploader.on('uploadBeforeSend', function (obj, data, headers) {
    297         $tr = $("#"+data.id);
    298         var file_name = $tr.find(".name").val();
    299         var class_id = $tr.find("select.class_id").val();
    300         var reg = /[1-9][0-9]*/g;
    301         data.md5 = md5Obj[ obj.file.id ];
    302         data.file_name = file_name;
    303         data.class_id = class_id;
    304         data.guid = data.guid + data.id.replace(/[^0-9]+/g, '');
    305     });
    306 
    307     uploader.on( 'uploadSuccess', function( file ,res) {
    308         if(res.code == 1){
    309             $( '#'+file.id ).find('.col4').text('成功上传');
    310             console.log(res);
    311             $('#'+file.id).find('.view').find('a').attr('href',playmain +'/?video_id='+ res.data.video_id);
    312         }else if(res.code == 2) {
    313             $( '#'+file.id ).find('.col4').text('视频已存在');
    314             console.log(res);
    315             $('#'+file.id).find('.view').find('a').attr('href',playmain +'/?video_id='+ res.data.video_id);
    316         }else {
    317             showError(res.msg);
    318         }
    319     });
    320 
    321     uploader.on( 'uploadError', function( file,reason ) {
    322         $( '#'+file.id ).find('.col4').text('上传失败');
    323         console.log(reason);
    324     });
    325 
    326     uploader.on( 'uploadComplete', function( file ) {
    327         $( '#'+file.id ).find('.progress').fadeOut();
    328     });
    329 
    330     uploader.on( 'all', function( type ) {
    331         if ( type === 'startUpload' ) {
    332             state = 'uploading';
    333         } else if ( type === 'stopUpload' ) {
    334             state = 'paused';
    335         } else if ( type === 'uploadFinished' ) {
    336             state = 'done';
    337         }
    338 
    339         if ( state === 'uploading' ) {
    340             window.is_confirm = true;
    341             $('.toBeUploaded').addClass("uploaded").removeClass("toBeUploaded");
    342             $('input.name').attr("disabled","disabled");
    343             $('input.col1__checkBox').hide();
    344             $('input').attr("disabled","disabled");
    345             $('select.class_id').attr("disabled","disabled");
    346             $('.btns__add').remove();
    347             $upload.addClass("btns__upload-ing").removeClass("btns__upload-start").html('<i class="btns__upload_icon"></i>正在上传视频');
    348 
    349         } else if(state === 'done') {
    350             window.is_confirm = false;
    351             console.log("上传完成");
    352             $upload.addClass("btns__upload-start btns__upload-refresh").removeClass("btns__upload-ing").html('<i class="btns__upload_icon"></i>开始上传视频');
    353         }
    354     });
    355 
    356     /**
    357      * 验证文件格式以及文件大小
    358      */
    359     uploader.on("error",function (type){
    360         var msg = ''
    361         switch (type){
    362             case "Q_TYPE_DENIED": msg = "请上传mp4格式文件";break;
    363             case "F_EXCEED_SIZE": msg = "文件大小不能超过1G";break;
    364             case "Q_EXCEED_NUM_LIMIT" : msg = "一次最多能上传10个文件";break;
    365             default: msg='';
    366         }
    367         if(msg != ''){
    368             showError(msg);
    369         }
    370     });
    371 
    372     $part_btn.on('click',function(){
    373         $('td .col1__checkBox').each(function(){
    374             if($(this).is(':checked')){
    375                 var $tr = $(this).parents('tr');
    376                 var id = $tr.attr('id');
    377                 uploader.removeFile( id );
    378             }
    379         });
    380     });
    381     $upload.on('click', function() {
    382         var isbreak = false;
    383         $(".name").each(function(){
    384             if(!$(this).val()|| $(this).val() == ''){
    385                 isbreak = true;
    386             }
    387         })
    388         if(isbreak){
    389             showError("文件名不能存在为空");
    390             return;
    391         }
    392         $("select.class_id").each(function(){
    393             if(!$(this).val()|| $(this).val() == ''){
    394                 isbreak = true;
    395             }
    396         })
    397         if(isbreak){
    398             showError("分类不能为空,请先添加分类");
    399             return;
    400         }
    401         if ( $(this).hasClass( 'btns__upload-refresh' ) ) {
    402             location.reload();
    403         }
    404         if ( $(this).hasClass( 'btns__upload-ing' ) ) {
    405             return false;
    406         }
    407         var md5Ready = true;
    408         $.each(md5Obj,function(index,item){
    409             if(!item || item==''){
    410                 md5Ready = false;
    411             }
    412         });
    413         if(!md5Ready){
    414             showError('文件尚未读取完成,请耐心等待');
    415             return false;
    416         }
    417         if ( state === 'ready' && md5Ready ) {
    418             uploader.upload();
    419         } else if ( state === 'paused' ) {
    420             uploader.upload();
    421         } else if ( state === 'uploading' ) {
    422             uploader.stop();
    423         }
    424     });
    425     $upload.addClass( 'state-' + state );
    426 
    427 });

    后台(PHP)【仅分片上传相关代码】

      1     public function action_upload_file(){
      2         $file_id = R::string('file_id', 'file');
      3         $keepFileName = R::string('keepFileName', 0);
      4         $unsize_change = R::numeric('unsize_change',0);
      5         $id = R::string('id');   //插件每上传一个视频自带id
      6         $guid = R::string('guid');  //标识视频
      7         $chunks = R::numeric('chunks');  // 分片数
      8         $chunk = R::numeric('chunk');  //分片号
      9         $file_name = R::string('file_name');
     10         $file = isset($_FILES[$file_id])?$_FILES[$file_id]:'';
     11         $md5 = R::string('md5');
     12         $this->upload = new Common_Upload();
     13 
     14         if(empty($guid) || empty($file_name) || empty($md5)){
     15             $this->response_msg(-1, 'guid 或 file_name 或 md5 不能为空');
     16             return;
     17         }
     18 
     19         if(empty($file['name'])){
     20             $this->response_msg(-1, '请上传一个文件');
     21             return;
     22         }else{
     23             if($chunks){
     24                 $res = $this->upload->saveFile_chunks($file,$chunks, $chunk, $guid);
     25                 if(empty($res)){
     26                     $this->response_msg(-2, '分片上传失败');
     27                     return;
     28                 }
     29 
     30             }else if($keepFileName){
     31                 $res = $this->upload->saveFile_nochunks($file, '', '', $keepFileName);
     32             }else{
     33                 $res = $this->upload->saveFile_nochunks($file);
     34             }
     35             if(empty($res)){
     36                 $err = $this->upload->getError();
     37                 $this->response_msg(-3, '上传文件出错!msg:'.print_r($err, true));
     38                 return;
     39             }
     40             if($unsize_change){
     41                 $size = $res['size'];
     42             }else{
     43                 $size = $this->convert_size($res['size']);
     44             }
     45 
     46             //视频上传完成
     47             if($chunks && $res['last_chunk']){
     48                 $domain = Kohana::$config->load('domain');
     49                 $video_domain = $domain[RUN_MOD]['VIDEO'];
     50 
     51                 if(!empty($file_name)){
     52                     $res['name'] = $file_name;
     53                 }
     54                 $video_data = array(
     55                     'video_name'=> $file_name,
     56                     'video_url'=> $video_domain."/".$res['path'],
     57                     'size'=>$res['size'],
     58                     'create_time'=> date('y-m-d H:i:s',time()),
     59                     'update_time'=> date('y-m-d H:i:s',time()),
     60                     'duration'=> $res['time'],
     61                     'md5'=>$md5
     62                 );
     63                 $video_mod = new Model_Videoinfo();
     64                 $video = $video_mod->save_video($video_data,$guid);
     65                 $res = array(
     66                     'path'=>$res['path'],
     67                     'chunks'=>$chunks,
     68                     'chunk'=>$chunk,
     69                     'size'=>$size,
     70                     'guid'=> $guid,
     71                     'video_id'=>$video[0],
     72                     'file'=>$file,
     73                     'id'=>$id
     74                 );
     75                 $this->response_msg(1,'视频上传成功',$res);
     76                 return;
     77             }
     78             //非分片上传
     79             if(!$chunks){
     80                 $domain = Kohana::$config->load('domain');
     81                 $video_domain = $domain[RUN_MOD]['VIDEO'];
     82                 if(!empty($file_name)){
     83                     $res['name'] = $file_name;
     84                 }
     85                 $video_data = array(
     86                     'video_name'=> $file_name,
     87                     'video_url'=> $video_domain."/".$res['path'],
     88                     'size'=>$res['size'],
     89                     'create_time'=> date('y-m-d H:i:s',time()),
     90                     'update_time'=> date('y-m-d H:i:s',time()),
     91                     'duration'=> $res['time'],
     92                     'md5'=>$md5
     93                 );
     94                 $video_mod = new Model_Videoinfo();
     95                 $video = $video_mod->save_video($video_data,$guid);
     96                 if(empty($video)){
     97                     $this->response_msg(-6, '视频信息保存失败');
     98                     return;
     99                 }
    100                 $res = array(
    101                     'path'=>$res['path'],
    102                     'video_data'=>$video_data,
    103                     'size'=>$size,
    104                     'guid'=> $guid,
    105                     'video_id'=>$video[0],
    106                     'file'=>$file,
    107                     'id'=>$id
    108                 );
    109                 $this->response_msg(1,'视频上传成功',$res);
    110                 return;
    111             }
    112             //分片上传成功(未全部分片上传完成)
    113             $res = array(
    114                 'chunks'=>$chunks,
    115                 'chunk'=>$chunk,
    116             );
    117             $this->response_msg(2, '分片上传成功',$res);
    118         }
    119     }
      1     /**
      2      * 保存分片文件(注意先验证文件是否合法)
      3      *
      4      * @param array $file 单个文件
      5      * @param string $attachdir 上传文件路径
      6      * @param string $upload_type 上传文件类型
      7      * @param bool $keepFileName 是否保持文件名,默认不不保持
      8      * @return bool
      9      */
     10     public function saveFile_chunks($file,$chunks, $chunk, $guid)
     11     {
     12         if(empty($guid) || empty($file) ){
     13             return false;
     14         }
     15         $file_name = (string)$guid . $chunk;
     16         //保存分片文件
     17         $file_info = $this->saveFile($file, '', '', false, $file_name,true);
     18         if ($file_info) {
     19             $cache = Cache::instance('memcache');
     20             //记录已上传的分片编号,上传顺序并不是按编号顺序进行上传
     21             $chunks_list_pre = $cache->get($guid);
     22             if(empty($chunks_list_pre)){
     23                 $strarr = array();
     24                 for($i=0;$i<$chunks;$i++){
     25                     $strarr[] = $guid.$i;
     26                 }
     27                 $cache->set($guid,$strarr,60 * 60 * 24);
     28             }
     29             $file_path = $cache->set($guid.$chunk,$file_info['path'],60 * 60 * 24);
     30 
     31             $chunk_path_array= array();
     32             for($i=0;$i<$chunks;$i++){
     33                 if($cache->get($guid.$i)){
     34                     $chunk_path_array[$i] = $cache->get($guid.$i);
     35                 }
     36             }
     37             list($Y,$M,$D,$H,$I,$S) = explode('-',date("Y-m-d-H-i-s", time()));
     38             $file_info['chunks_path_count'] = count($chunk_path_array);
     39             $file_info['last_chunk'] = false;
     40             if (count($chunk_path_array) == $chunks) {
     41                 //按目录类型存储
     42                 $dirType = substr($file_info['type'], 1, strlen($file_info['type']));;
     43                 //目录类型前面加上前缀url
     44                 $dirType = $this->pre_url.$dirType;
     45                 //按年月二级存储
     46                 $month_file_path = $Y.'/'.$M;
     47                 $saveName ='upload/mp4/'.$month_file_path.'/original/'.$guid.$file_info['type'];
     48                 $join_file_name =$this->attachDIR.$saveName;
     49                 if(!is_dir($this->attachDIR.'upload/mp4/'.$month_file_path.'/original/')){
     50                     mkdir($this->attachDIR.'upload/mp4/'.$month_file_path.'/original/',0755,true);
     51                 }
     52                 if(! file_exists($join_file_name)){
     53                     $fp = fopen($join_file_name, "ab");
     54                     //合并过程中对文件加锁,防止同时操作而出错
     55                     if (flock($fp,LOCK_EX)){
     56                         for ($i = 0; $i < $chunks; $i++) {
     57                                 $tmp_file = $this->attachDIR . $chunk_path_array[$i];
     58                                 $handle = fopen($tmp_file, "rb");
     59                                 fwrite($fp, fread($handle, filesize($tmp_file)));
     60                                 fclose($handle);
     61                                 unset($handle);
     62                                 unlink($tmp_file);//合并完毕的文件就删除
     63                         }//组装分片
     64                         $cache->delete($guid);
     65                         for($i=0;$i<$chunks;$i++){
     66                             $cache->delete($guid.$i);
     67                         }
     68                         $time = $this->getTime($join_file_name,$file_info['type']);
     69                         $file_info['time'] = $time;
     70                         $file_info['path'] = $saveName;
     71                         $file_info['size'] = filesize($join_file_name);
     72                         $file_info['last_chunk'] = true;
     73 
     74                         $model_mod = new Model_Base();
     75                         $model_mod->disconnect();
     76                         $pid = pcntl_fork();
     77                         //父进程和子进程都会执行下面代码
     78                         if ($pid == -1) {
     79                             //错误处理:创建子进程失败时返回-1.
     80                             die('could not fork');
     81                         } else if ($pid) {
     82                             $model_mod->connect();
     83                             //对上传完成的视频进行排队转码
     84                             $this->thread($join_file_name,$file_info['type'],$guid);
     85                             //父进程会得到子进程号,所以这里是父进程执行的逻辑
     86                             pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。
     87                         } else {
     88                             return $file_info;
     89                             //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
     90                         }
     91 
     92                     }
     93                 }
     94 
     95             }
     96             return $file_info;
     97         } else {
     98                 $this->error[] = '分片上传失败';
     99                 return false;
    100         }
    101             /*}}}*/
    102         }

    1,实现了分片上传;

    2,同时在上传前检查视频md5 是否在库,如已存在可实现“秒传” 功能,即直接复制数据信息,指向同一个文件,不必再上传;

    3,可实现断点续传,上传过程中中断;之前上传的分片已保留在服务器,只需重新上传尚未上传的分片即可;

  • 相关阅读:
    Leetcode 70 Climbing Stairs 递推
    Leetcode 83 Remove Duplicates from Sorted List 链表
    Leetcode 328 Odd Even Linked List 链表
    Leetcode 206 Reverse Linked List 链表
    Spring中注解
    SpringMVC配置文件
    java设计模式----单例模式
    java设计模式----工厂方法模式
    java设计模式----装饰者模式
    java设计模式----观察者模式
  • 原文地址:https://www.cnblogs.com/AllenChou/p/7852719.html
Copyright © 2011-2022 走看看