php 分片上传文件 + JQ实现

function uploadFile($name,$savedir = 'video')
    {
        $return_arr = array('0','');
        $userinfo = $this->userinfo;
        $userID = $userinfo['user_id']; //用户标识
        if(!empty($_POST['act']) && trim($_POST['act'])=='combine'){
            //合并文件
            $chunks = intval($_POST['chunks']);//总分块个数
            $time = intval($_POST['time']);
            //文件后缀
            $type = substr($_POST['filename'],strripos($_POST['filename'],'.'));
            //限制文件格式
//            if (!in_array($type,['.mp4','.avi'])) {
//                abort(403, '文件格式不允许', []);
//            }
            //保存临时文件
            $tmppath = $savedir.'/tmp'; //临时目录,
            if(!file_exists($tmppath)){ @mkdir($tmppath,0777,true); }

            $filenamemd5 = md5($_POST['filename'].$time);

            $savedir = $savedir.'/files/'.date('Ym',time());
            if(!file_exists($savedir)){ @mkdir($savedir,0777,true);}
            $newname = date('mdH',time()).rand(10000,99999).'_'.rand(100000,999999).$type;

            $writer = fopen("{$savedir}/{$newname}","ab"); //合并后的文件名
            for($i=0;$i<$chunks;$i++){
                $file2read = "{$tmppath}/{$userID}_{$filenamemd5}_{$i}";
                $reader = fopen($file2read,"rb");
                fwrite($writer,fread($reader,filesize($file2read)));
                fclose($reader);
                unset($reader);
                @unlink($file2read);//删除分块临时文件
            }
            fclose($writer);
            $return_arr[0]='1';
            $return_arr[1]="{$savedir}/{$newname}";

        }else{
            if(empty($_FILES[$name]) || $_FILES[$name]["error"] > 0){
                return array( '0','上传失败' );
            }

            //保存临时文件
            $chunks = intval($_POST['chunks']);//总分块个数
            $chunk  = intval($_POST['chunk']);//当前分块索引
            $time = intval($_POST['time']);
            //临时目录
            $tmppath = $savedir.'/tmp';
            if(!file_exists($tmppath)){ @mkdir($tmppath,0777,true); }

            $filenamemd5 = md5($_POST['filename'].$time);
            $tmpname = "{$userID}_{$filenamemd5}_{$chunk}";//临时文件名
            @move_uploaded_file($_FILES[$name]["tmp_name"],"{$tmppath}/{$tmpname}");

            $return_arr[0]='1';
            $return_arr[1]='';
        }

        return $return_arr;
    }

前端调用

<!DOCTYPE html>
<html>
<head>
    <title>PHP大文件分片上传</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<style type="text/css">
    #percent-bg{position:relative;width:100%;height:20px;border:1px solid #ccc;}
    #percent{position:absolute;display:block;width:0;height:100%;left:0;background:green;z-index:100;}
    #percent_num{position:absolute;display:block;width:30px;height:100%;left:50%;margin-left:-15px;z-index:200; text-align:center;}
</style>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
<script type="text/javascript">
    var pz = {
        "maxSize":1024*1024*2000, //文件大小限制20M
        "bufferSize":1024*512, //单个分片大小
        "blocks":[], //分片数据集合
        "blockName":"newfile",//表单 file 的名字
        "filename":"",//上传的本地文件真实文件名
        "threadNum":2,//上传线程数量
        "reset":function(fname){
            this.blocks = [];
            this.filename = fname;
        }
    }
    function afterselect(){
        if(document.getElementById("x").files.length<1){
            return false;
        }
        var file = document.getElementById("x").files[0];
        if(file.size>pz.maxSize){
            showmsg("文件大小限制:"+pz.maxSize+",实际文件大小:"+file.size);
            return;
        }
        pz.reset(file.name);
        pz.threadNum = parseInt($("#threadCount").val());//设定线程数量


        __index = __activeThreadCount = __sendedBlockCount = 0;

        var endByte = 0,startByte = 0;

        while(true){
            startByte = endByte;
            if(endByte + pz.bufferSize >= file.size){
                endByte = file.size;
            }else{
                endByte = endByte+pz.bufferSize;
            }
            var block = sliceFile(file,startByte,endByte);
            if(!block){
                showmsg("分片失败");
                return;
            }
            pz.blocks.push(block);
            if(endByte >= file.size){ break; }
        }
        showmsg("文件名:"+file.name);
        showmsg("文件大小:"+file.size);
        showmsg("总分片数量:"+pz.blocks.length);

        var dis = Math.min(pz.threadNum,pz.blocks.length);
        for(var i=0;i<dis;i++){
            (function(i){
                setTimeout(function(){
                    __activeThreadCount++;
                    fenpei(i);
                },500);
            })(i);
        }
        showmsg("启动线程:"+dis+" 个");
        showmsg("------------------------");
    }

    //只要是操作主线程中的变量或其他,就一定会自动同步到主线程来。相当于自动lock()
    var __index = 0;
    var __activeThreadCount = 0; //当前活跃线程数量
    var __sendedBlockCount = 0; //已上传的block数量
    var time = new Date(); //此次上传辅助标识
    function fenpei(i){
        //参数:线程名
        if(__index>=pz.blocks.length){
            showmsg("线程"+i+' 结束');
            __activeThreadCount--;
            if(__activeThreadCount==0){
                showmsg("------------------------");
                showmsg('多线程分片上传完毕,正在处理分片数据...');
                combineFile();
            }
            return;
        }
        uploadBlock(i,__index);
        __index++;
    }
    //发送拼接分块命令
    function combineFile(){
        $.post(
            '/index_h5/upload/upVideo',
            {"act":"combine","chunks":pz.blocks.length,"filename":pz.filename,"time":time},
            function(e){
                if(e.flag){
                    showmsg("分片数据处理完成,任务结束,URl:"+e.url);
                }else{
                    showmsg(e.info);
                }
            },'json'
        );
    }
    //显示进度
    function showPercent(){
        var percent = parseInt(__sendedBlockCount / pz.blocks.length *100);
        if(percent>100){ percent = 100;	}

        $("#percent").stop(true,true).animate({"width":percent+"%"},10);
        $("#percent_num").html(percent+"%");
    }
    //上传一个分片
    function uploadBlock(i,index){
        var chunk = index;
        showmsg('线程'+i+' 分片'+chunk+' start');
        var fd = new FormData();
        fd.append("newfile", pz.blocks[chunk]);//文件
        fd.append("filename", pz.filename);//文件名
        fd.append("chunk", chunk);//分片索引
        fd.append("chunks", pz.blocks.length);//分片总数
        fd.append("time", time);//分片总数
        $.ajax({
            url:'/index_h5/upload/upVideo',
            type:"post",
            data: fd,
            dataType:"json",
            cache: false,
            contentType:false,
            processData:false,//设置为true的时候,jquery ajax 提交的时候不会序列化 data,而是直接使用data
            success:function(data){
                //timer线程中的异步,此时同步了
                showmsg("线程"+i+" 分片"+chunk+" end");
                __sendedBlockCount++;
                showPercent();
                fenpei(i);
            },
            complete:function(XMLHttpRequest,textStatus){
            },
            error:function(){}
        });
    }
    //分割file成分片
    function sliceFile(file,startByte,endByte){
        if(file.slice){
            return  file.slice(startByte,endByte);
        }
        if(file.webkitSlice){
            return  file.webkitSlice(startByte,endByte);
        }
        if(file.mozSlice){
            return  file.mozSlice(startByte,endByte);
        }
        return null;
    }
    //输出消息
    function showmsg(msg){
        var _t = $("#msg").val();
        _t += msg+"\r\n";
        $("#msg").val(_t);
        var scrollTop = $("#msg")[0].scrollHeight;
        $("#msg").scrollTop(scrollTop);
    }
</script>
<body>
<h2>jQuery+PHP实现对大文件分片后多线程上传,返回上传后的URL</h2>
<p>选择文件后自动开始上传</p>
<p>线程数:
    <select id="threadCount" style="height:28px;">
        <option value="1">1</option>
        <option value="2" selected="selected">2</option>
        <option value="3">3</option>
        <option value="5">5</option>
        <option value="10">10</option>
    </select>
</p>
<input type="file" id="x" onchange="afterselect();">
<p></p>
<textarea id="msg" style="width:99%;height:400px;overflow-y:auto;"></textarea>
<p></p>
<div id="percent-bg">
    <span id="percent"></span>
    <span id="percent_num">0%</span>
</div>
</body>
</html>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值