首页

ajax文件上传

kkcode
2018-05-22  阅读 717

iframe模拟Ajax文件上传效果

分析:

1:捕捉表单提交的动作

2:创建一个iframe

3:把表单的 target修改 指向 该iframe

4:去掉这个iframe

<div id="progress"></div>
<form action="up.php" method="post" enctype="multipart/form-data" onsubmit="return ajaxup();">
    <input type="file" name="pic">
    <input type="submit" value="提交">
</form>
<script>
    function ajaxup() {
        var ifname = 'up' + Math.random();
        $('<iframe name="' + ifname + '" width="0" height="0" frameBorder="0"></iframe>').appendTo($('body'));
        $('form:first').attr('target', ifname);
        $('#progress').html('<img src="./loading.gif" />');
    }
</script>复制代码
sleep(3);

if(empty($_FILES)) {
    exit('no file');
}

$msg = $_FILES['pic']['error']==0 ? 'succ' : 'fail';

echo "<script>parent.document.getElementById('progress').innerHTML = '$msg'</script>";
复制代码

formData发送数据

formData "表单数据"对象,这是在html5中新增的一个Api。 他能以表单对象做参数,自动的把表单的数据打包. 当ajax发送数据时,发送些formData,达到发送表单内各数据项的目的。

// 02-testpost.php
print_r($_POST);复制代码
<form id="tform">
    用户名:<input type="text" name="username"><br>
    年 龄:<input type="text" name="age"><br>
    email:<input type="text" name="email"><br>
    性 别:<input type="text" name="gender"><br>
    <input type="button" value="ajax发送" onclick="send();">
</form>
<div id="debug"></div>
<script type="text/javascript">
    function send() {
        var fm = document.getElementById('tform');
        var fd = new FormData(fm);

        var xhr = new XMLHttpRequest();
        xhr.open('POST', '02-testpost.php', true);

        xhr.onreadystatechange = function () {
            if (this.readyState == 4) {
                document.getElementById('debug').innerHTML = this.responseText;
            }
        }

        xhr.send(fd);

        // 看下面这个例子,说明formData对象既可以从表单生成数据,也可以手动的append
        var fd2 = new FormData();
        fd2.append('username', 'zhangsanm');
        fd2.append('age', 23);
        xhr.send(fd2);
    }
</script>复制代码

把二进制对象转换成浏览器显示的资源

<input type="file" name="pic" onchange="selfile();">
<div id="debug"></div>
<script type="text/javascript">
    function selfile() {
        var pic = document.getElementsByTagName('input')[0].files[0];
        console.log(pic);
        var debug = document.getElementById('debug');
        var content = '';
        content += '文件名称:' + pic.name + '<br />';
        content += '文件大小' + pic.size + '<br />';
        debug.innerHTML = content;

        var tmpimg = document.createElement('img');
        tmpimg.style.width = '300px';
        tmpimg.src = window.URL.createObjectURL(pic); // 把二进制对象直接读成浏览器显示的资源
        document.getElementsByTagName('body')[0].appendChild(tmpimg);
    }
</script>复制代码

pic变量打印值,如下图所示: pic变量打印值

通过form表单把图片发送到服务器

<input type="file" name="pic" onchange="selfile();">
<div id="debug"></div>
<script type="text/javascript">
    function selfile() {
        var fd = new FormData();
        var pic = document.getElementsByTagName('input')[0].files[0];
        // 把文件内容追加到表单数据里
        fd.append('pic', pic);

        var xhr = new XMLHttpRequest();
        xhr.open('POST', '04-upfile.php', true);

        xhr.onreadystatechange = function () {
            if (this.readyState == 4) {
                document.getElementById('debug').innerHTML = this.responseText;
            }
        }

        xhr.send(fd);
    }
</script>复制代码

把图片移动到上传目录

if(empty($_FILES)) {
    exit('no file');
}

if($_FILES['pic']['error'] != 0) {
    exit('fail');
}

move_uploaded_file($_FILES['pic']['tmp_name'],'upload/'.$_FILES['pic']['name']);

echo 'ok';复制代码

上传显示进度条

进度条需要2个最基础的信息--- 总大小,已上传大小

解决: 在html5,有一个"上传过程"的事件--onprogress事件中可以读到这2个信息

具体思路: 在上传过程中,不断的触发函数,函数读取已上传/总大小,更新页面的进度条。

<style type="text/css">
    #progress {
      width: 500px;
      height: 30px;
      border: 1px solid green;
    }

    #bar {
      width: 0%;
      height: 100%;
      background: green;
    }
</style>
<div id="progress"><div id="bar"></div></div>
<input type="file" onchange="selfile();">
<script type="text/javascript">
    function selfile() {
      var fd = new FormData();
      var video = document.getElementsByTagName('input')[0].files[0];
      fd.append('video', video);

      var xhr = new XMLHttpRequest();
      xhr.open('POST', 'upload.php', true);


      // 利用XHR2的新标准,为上传过程,写一个监听函数
      xhr.upload.onprogress = function (ev) {
        console.log(ev);
        if (ev.lengthComputable) {
          var percent = 100 * ev.loaded / ev.total;
          document.getElementById('bar').style.width = percent + '%';
          document.getElementById('bar').innerHTML = parseInt(percent) + '%';
        }
      }

      xhr.send(fd);
    }
</script>复制代码
// upload.php
header('Access-Control-Allow-Origin: *');
// 如果接收不到文件
// 请确定 post_max_size upload_max_filesize 的大小(php.ini中)
$upfile = $_FILES['video'];

function upload_file($files, $path = "./upload", $imagesExt = ['jpg', 'png', 'jpeg', 'gif','avi', 'mp4','wmv'])
{
    // 判断错误号
    if (@$files['error'] == 00) {
        // 判断文件类型
        $ext = strtolower(pathinfo(@$files['name'], PATHINFO_EXTENSION));
        if (!in_array($ext, $imagesExt)) {
            return "非法文件类型";
        }

        // 判断是否存在上传到的目录
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }

        // 生成唯一的文件名
        $fileName = md5(uniqid(microtime(true), true)) . '.' . $ext;

        // 将文件名拼接到指定的目录下
        $destName = $path . "/" . $fileName;

        // 进行文件移动
        if (!move_uploaded_file($files['tmp_name'], $destName)) {
            return "文件上传失败!";
        }
        return "文件上传成功!";
    } else {
        // 根据错误号返回提示信息
        switch (@$files['error']) {
            case 1:
                echo "上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值";
                break;
            case 2:
                echo "上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值";
                break;
            case 3:
                echo "文件只有部分被上传";
                break;
            case 4:
                echo "没有文件被上传";
                break;
            case 6:
            case 7:
                echo "系统错误";
                break;
        }
    }

}

echo upload_file($upfile);复制代码

HTML5大文件切割上传

File 继承自 Blob,Blob有slice方法,可以截取二进制对象的一部分。

思路:

1.截取10M,上传

2.判断文件有没有上传完毕。没有,继续重复步骤1、2

<style type="text/css">
    #progress {
        width: 500px;
        height: 30px;
        border: 1px solid green;
    }

    #bar {
        width: 0%;
        height: 100%;
        background: green;
    }
</style>
<div id="progress">
    <div id="bar"></div>
</div>
<input type="file" name="mov" onchange="fire();">
<script type="text/javascript">
var xhr = new XMLHttpRequest(),clock = null;

function fire() {
    clock = window.setInterval(sendfile,1000);
}

// 闭包计数器
var sendfile = (function() {

    const LENGTH = 10 * 1024 * 1024; //每次切10M 
    var sta = 0;
    var end = sta + LENGTH;
    var sending = false; // 标志正在上传中

    var blob = null;
    var fd = null;

    // 百分比 
    var percent = 0;


    return (function () {       
        if(sending == true) {
            return;
        }

        var mov = document.getElementsByName('mov')[0].files[0];
        // 如果sta>mov.size,就结束了
        if(sta > mov.size) {
            clearInterval(clock);
            return;
        }

        blob = mov.slice(sta,end);

        fd = new FormData();
        fd.append('part',blob);

        up(fd);

        sta = end;
        end = sta + LENGTH;
        sending = false; // 上传完了


        percent = 100 * end / mov.size;
        if(percent > 100) {
            percent = 100;
        }
        document.getElementById('bar').style.width = percent + '%';
    });

})();

function up(fd) {
    xhr.open('POST','06-sliceup.php',false);
    xhr.send(fd);
}
</script>复制代码
// 06-sliceup.php
// 接收文件并合并
if(!file_exists('./upload/hhr.mov')) {
    move_uploaded_file($_FILES['part']['tmp_name'],'./upload/hhr.mov');
} else {
    file_put_contents('./upload/hhr.mov',file_get_contents($_FILES['part']['tmp_name']),FILE_APPEND);
}

echo 'ok';复制代码
本文为作者原创文章,转载无需和我联系,但请注明转载链接。 【前端黑猫】