JS 异步分段上传文件

Wesley13
• 阅读 693

为了解决大文件上传 (PHP上传最大限制2GB)

同时为了解决文件上传是对服务器造成的压力

可以通过分段上传解决这个问题,这得益于HTML5开发的file API

前台代码:

引用了进度条插件myProgress.js

<link href="__PUBLIC__/admin/css/myProgress.css" rel="stylesheet"> 
<script src="__PUBLIC__/admin/js/jquery.myProgress.js"></script>

<div>
    <div>
        <form id="myForm">
          <div>
            //上传文件时由用户指定文件名
            <label for="FileName">File Name</label>
            <input type="text" name="title" class="form-control" id="FileName">
          </div>
          <div>
            <label for="myFile">Chose File</label>
            <input type="file" id="myFile">
            <div class="progress-out" id="progress">
                <div class="percent-show"><span>0</span>%</div>
                <div class="progress-in"></div>
            </div>
          </div>
        </form>
        <button type="button" class="btn btn-default" id="btn">Submit</button>
    </div>
</div>
<script>
//初始化上传
function initUpload() {
    var chunk = 1000*1024;   //每片大小
    var input = document.getElementById("myFile");  //input file
    input.onchange = function (e) {
        //获得上传的文件
        var file = this.files[0];
        //如果大于指定大小 提示错误
        if(file.size > 1*1024*1024*1024){
            $('#help_msg').removeClass('help-block').addClass('error-block');
            return ;
        }else{
            $('#help_msg').css('display','none');
        }
        // 开启进度条
        $("#progress").css('display','block');
        $("#progress").myProgress({speed: 1000, percent: 0, width: "200px", height: "12px"});
        
        var query = {};
        var chunks = [];

        if (!!file) {
            var start = 0;
            //文件分片
            for (var i = 0; i < Math.ceil(file.size / chunk); i++) {
                //最后一段取文件的真实大小
                var end = 0;
                if(i == (Math.ceil(file.size / chunk)-1)){
                    end = file.size;
                }else{
                    end = start + chunk;
                }
                chunks[i] = file.slice(start , end);
                start = end;
            }
            
            // 采用post方法上传文件
            // url query上拼接以下参数,用于记录上传偏移
            // post body中存放本次要上传的二进制数据
            query = {
                fileName : file.name,
                fileSize: file.size,
                dataSize: chunk,
                nextOffset: 0
            }

            upload(chunks, query, successPerUpload);
        }
    }
}

// 执行上传
function upload(chunks, query, cb) {
    //对象转字符串 用&连接
    var queryStr = Object.getOwnPropertyNames(query).map(key => {
        return key + "=" + query[key];
    }).join("&");

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/Shop/index.php/Admin/File/upload_file?" + queryStr);
    xhr.overrideMimeType("application/octet-stream");
    
    //获取post body中二进制数据
    var index = Math.floor(query.nextOffset / query.dataSize);
    getFileBinary(chunks[index], function (binary) {
        if (xhr.sendAsBinary) {
            xhr.sendAsBinary(binary);
        } else {
            xhr.send(binary);
        }

    });

    xhr.onreadystatechange = function (e) {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                var resp = JSON.parse(xhr.responseText);
                //通过返回数据更新进度条
                var precent = Math.ceil((resp.offset / query.fileSize) * 100);
                $("#progress").myProgress({speed: 1000, percent: precent, width: "200px", height: "12px"});
                // 接口返回nextoffset
                // resp = {
                //     isFinish:false,
                //     offset:100*1024
                // }
                if (typeof cb === "function") {
                    cb.call(this, resp, chunks, query)
                }
            }
        }
    }
}

// 每片上传成功后执行
function successPerUpload(resp, chunks, query) {
    if (resp.isFinish === true) {
        //上传完成给出提示
        $('#help_msg').css('display','block').addClass('error-block').html('success!');
    } else {
        //未上传完毕
        query.nextOffset = resp.offset;
        upload(chunks, query, successPerUpload);
    }
}

// 获取文件二进制数据
function getFileBinary(file, cb) {
    var reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = function (e) {
        if (typeof cb === "function") {
            cb.call(this, this.result);
        }
    }
}
//初始化上传
initUpload();

//ajax模拟提交表单
$(function(){
    $('#btn').click(function(){
        var fd = new FormData(document.querySelector('#myForm'));
        var input = document.getElementById("myFile");    //input file
        var file = input.files[0];
        if(!file){
            $('#help_msg').css('display','block').addClass('error-block').html('please chose the file !');
            return ;
        }
        fd.append('FileName',file.name);
        fd.append('size',file.size);
        $.ajax({
            url : "/Shop/index.php/Admin/File/add",
            type: "POST",
            async : true,
            data: fd,
            processData: false,  // 不处理数据
            contentType: false,   // 不设置内容类型
            success : function(result){
                console.log(result);
                if(result.res == 1){
                    window.location.href = "http://localhost:8080/Shop/index.php/Admin/File/index";
                }else{
                    $("#help_msg_1").css('display','block').html('upload faild ' + $result.msg);
                }
            }
        });
    })
})

</script>

后台PHP代码   

public function add(){
        if($_POST){
            $Attach = D('Attachment');
            $file_path = './Upload/File/'.$_POST['FileName'];
            //如果是win系统将文件名改成GBK编码
            if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){
                $file_path = iconv('UTF-8', 'GBK', $file_path);
            }
            if(file_exists($file_path)){
                //获得拓展名
                $ext = strtolower(trim(substr(strrchr($_POST['FileName'], '.'), 1)));
                //生成新的文件名
                $url = './Upload/File/'.date("Ymdhms").rand(1000,9999).'.'.$ext;
                $_POST['url'] = $url ;
                //将上传的文件改名,将新的路径存入数据库
                if(rename($file_path, $url)){
                    $res = $Attach -> add_file($_POST);
                    if($res['res']){
                        $log['remark'] = session('userinfo')['name'].'在'.date("Y-m-d H:i:s").'上传了文件';
                        D('ActionLog') -> add_log($log);
                        $this -> ajaxReturn(array('res' => 1));
                    }else{
                        $this -> ajaxReturn(array('res' => 0 , 'msg' => $res['msg']));
                    }
                }
            }else{
                $this -> ajaxReturn(array('res' => 0, 'msg' => '上传文件不存在'));
            }    
        }else{
            $this -> show();
        }
    }
    //异步分段上传文件
    public function upload_file(){
        
        $path = './Upload/File/'.$_GET['fileName'];
        if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){
            $path = iconv('UTF-8', 'GBK', $path);
        }
        if(!file_exists($path)){
            $handle = fopen($path, "a+");
            fclose($handle);
        }
        file_put_contents($path, file_get_contents('php://input'),FILE_APPEND|LOCK_EX);
        $offset = filesize($path);
        if( $offset >= $_GET['fileSize'] ){
             $this -> ajaxReturn(array('isFinish' => true));
        }else{
            $this -> ajaxReturn(array('isFinish' => false , 'offset' => $offset));
        }
    }
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这