6

在以往的项目,用过三种方式的文件上传,分别是xhr、webuploader、plupload,好记性不如烂笔头,在开始整理笔记的时候,文件上传的优先级就排得敲级高,毕竟特别常用嘛~表达能力有限,只能无限开门放代码。。。


xhr

这个方法的兼容性不太好。

XMLHttpRequest一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了XMLHttpRequest标准。XMLHttpRequest标准又分为Level 1和Level 2。
XMLHttpRequest Level 1主要存在以下缺点:
受同源策略的限制,不能发送跨域请求;
不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据;
在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成;

Level 2对Level 1 进行了改进,XMLHttpRequest Level 2中新增了以下功能:
可以发送跨域请求,在服务端允许的情况下;
支持发送和接收二进制数据;
新增formData对象,支持发送表单数据;
发送和获取数据时,可以获取进度信息;
可以设置请求的超时时间;

图片是XmlHttpRequest Level 2的兼容性
图片描述

从图中可以看到:

IE8/IE9、Opera Mini 完全不支持xhr对象
IE10/IE11部分支持,不支持 xhr.responseType为json
部分浏览器不支持设置请求超时,即无法使用xhr.timeout
部分浏览器不支持xhr.responseType为blob

以上xhr兼容性描述是引用自https://segmentfault.com/a/11...

以下是我之前写的有进度条的文件上传的demo,里面有css样式,代码比较长。通过给xhr对象添加监听事件,比如beforeSend事件初始化进度条,progress事件返回上传进度等。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name=”renderer” content="webkit|ie-comp|ie-stand">
    <meta name="description" content="">
    <meta name="keywords" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>运单上传</title>
    <style>
        html, body {
            width: 100%;
            height: 100%;
            padding: 0;
            margin: 0;
            overflow: hidden;
        }
        body {
            background: #b4e1f4;
            position: relative;
            font-family: '微软雅黑';
        }
        .upload-cont {
            height: 270px;
            width: 500px;
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            margin: auto;
            background: #f5f1e8;
            -webkit-border-radius: 5px;
            -moz-border-radius: 5px;
            border-radius: 5px;
            -moz-box-shadow: 1px 1px 10px #888888;
            -webkit-box-shadow: 1px 1px 10px #888888;
            box-shadow: 1px 1px 10px #888888;
        }
        .upload-title {
            height: 60px;
            line-height: 60px;
            color: #fff;
            font-size: 18px;
            padding-left: 20px;
            background: -moz-linear-gradient(top, #ee8f57 0%, #ea7047 100%);
            background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ee8f57), color-stop(100%,#ea7047));
            background: -webkit-linear-gradient(top, #ee8f57 0%,#ea7047 100%);
            background: -o-linear-gradient(top, #ee8f57 0%,#ea7047 100%);
            background: -ms-linear-gradient(top, #ee8f57 0%,#ea7047 100%);
            background: linear-gradient(to bottom, #ee8f57 0%,#ea7047 100%);
            -webkit-border-top-left-radius: 5px;
            -moz-border-radius-topleft: 5px;
            border-top-left-radius: 5px;
            -webkit-border-top-right-radius: 5px;
            -moz-border-radius-topright: 5px;
            border-top-right-radius: 5px;
        }
        .upload-form {
            padding: 15px 20px;
        }
        .upload-hint {
            color: #48b8e0;
        }
        .file-cont {
            width: 325px;
            height: 38px;
            -webkit-border-radius: 3px;
            -moz-border-radius: 3px;
            border-radius: 3px;
            border: 1px solid #d3d3d3;
            vertical-align: middle;
            margin: 20px 0;
            padding: 0 10px;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }
        .select-btn {
            position: relative;
            display: inline-block;
            vertical-align: middle;
            margin: 20px 0 20px 5px;
        }
        .select-btn input[type=file] {
            width: 100px;
            height: 40px;
            position: relative;
            z-index: 9;
            opacity: 0;
            cursor: pointer;
        }
        .select-btn label {
            position: absolute;
            display: inline-block;
            color: #fff;
            width: 100px;
            height: 40px;
            line-height: 40px;
            text-align: center;
            top: 0;
            left: 0;
            background: #ea7047;
            -webkit-border-radius: 3px;
            -moz-border-radius: 3px;
            border-radius: 3px;
        }
        .cut-line {
            border-top: 1px solid #d3d3d3;
        }
        .progress-bar {
            height: 22px;
            width: 345px;
            margin-top: 18px;
            display: inline-block;
            position: relative;
            border: 1px solid #d3d3d3;
            -webkit-border-radius: 3px;
            -moz-border-radius: 3px;
            border-radius: 3px;
            -moz-box-shadow:1px 1px 8px #888888 inset;
            -webkit-box-shadow:1px 1px 8px #888888 inset;
            box-shadow:1px 1px 8px #888888 inset;
        }
        .progress-bar-inner {
            position: absolute;
            top: 0;
            left: 0;
            width: 0%;
            height: 22px;
            text-align: center;
            -webkit-border-radius: 3px;
            -moz-border-radius: 3px;
            border-radius: 3px;
            background: -moz-linear-gradient(top, #ee8f57 0%, #ea7047 100%);
            background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ee8f57), color-stop(100%,#ea7047));
            background: -webkit-linear-gradient(top, #ee8f57 0%,#ea7047 100%);
            background: -o-linear-gradient(top, #ee8f57 0%,#ea7047 100%);
            background: -ms-linear-gradient(top, #ee8f57 0%,#ea7047 100%);
            background: linear-gradient(to bottom, #ee8f57 0%,#ea7047 100%);
        }
        .upload-btn {
            width: 100px;
            height: 40px;
            margin-top: 10px;
            background: #48b8e0;
            -webkit-border-radius: 3px;
            -moz-border-radius: 3px;
            border-radius: 3px;
            border: none;
            color: #fff;
            font-size: 16px;
            cursor: pointer;
            float: right;
        }
        .mask-bg {
            position: absolute;
            top: 0;
            background: #000;
            opacity: 0.3;
            width: 100%;
            height: 100%;
            z-index: 1000;
        }

        .hint-cont {
            z-index: 1100;
            background: #fff;
            width: 300px;
            height: 120px;
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            margin: auto;
            text-align: center;
            border-radius: 5px;
        }
        .hint-title {
            display: inline-block;
            height: 40px;
            line-height: 40px;
        }
        .hint-close {
            float: right;
            margin-right: 10px;
            font-size: 25px;
            cursor: pointer;
        }
        .hint-content {
            margin-top: 10px;
        }
    </style>
</head>
<body>
<div class="upload-cont">
    <div class="upload-title">上传文件</div>
    <form id= "uploadForm" class="upload-form">
        <div class="upload-hint">提示:请上传excel文件</div>
        <div>
            <input type="text" class="file-cont" disabled>
            <div class="select-btn">
                <input id="fileUpload" type="file" name="file"/>
                <label>选择文件</label>
            </div>
        </div>
        <hr class="cut-line">
        <div id="progressBar" class="progress-bar">
            <div class="progress-bar-inner"></div>
        </div>
        <input type="button" value="上传" class="upload-btn" onclick="doUpload()" />
    </form>
    <!----------------------------------->
    <br>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
    var resultData;
    function doUpload(){
        var formData = new FormData($( "#uploadForm" )[0]);

        var xhr = new XMLHttpRequest();
        // 上传前初始化
        xhr.addEventListener('beforeSend', function () {
            $('.progress-bar-inner').css('width', '0%').text('');
        });

        //上传中设置上传的百分比
        xhr.upload.addEventListener("progress", function(evt){
            if (evt.lengthComputable) {
                var percentComplete = Math.round(evt.loaded * 100 / evt.total);
                $('.progress-bar-inner').css('width', percentComplete+"%").text(percentComplete+"%");
            }else {
                $('#progressBar').text('无法计算');
            }
        }, false);

        //请求完成后执行的操作
        xhr.addEventListener("load", function(evt){
            var message = evt.target.responseText, obj = eval("("+message+")");
            if(obj.status == 1){
                showHintDialog(evt);
            }else{
                showHintDialog(obj.message);
            }

        }, false);
//请求error
        xhr.addEventListener("error", uploadFailed, false);
//请求中断
        xhr.addEventListener("abort", uploadCanceled, false);
//发送请求
        xhr.open("POST", "../excel/in/");
        xhr.send(formData);
        function uploadFailed(evt) {
            showHintDialog("上传出错");
        }

        function uploadCanceled(evt) {
            showHintDialog("上传已由用户或浏览器取消删除连接");
        }
    }
    function showHintDialog (str) {
        var urlHtml;
        if (typeof str == 'object') {
            if(!str.success) {
                if (str.errorMessage.startsWith('http')) {
                    urlHtml = '<a href=" ' + str.errorMessage + '"onclick="closeHint()" target="_blank" style="color:red">有错误运单数据,请点击下载</a>';
                } else {
                    urlHtml = '<p style="color: red">' + str.errorMessage + '</p>';
                }
            }else{
                urlHtml = '<p style="color: black">文件上传成功 </p>';
            }
        }
        var dialogHtml = '<div id="alertStrCont"><div class="mask-bg" onclick="closeHint()"></div><div class="hint-cont"><span class="hint-title">提示</span><span class="hint-close" onclick="closeHint()">×</span><div class="hint-content">'+urlHtml+'</div></div></div>';
        $('body').append(dialogHtml);
    }
    function closeHint() {
        $('#alertStrCont').remove();
    }
    $('#fileUpload').on('change', function () {
        if (this.files.length === 0) { return; }
        var oFile = this.files[0];
        $('.file-cont').val(oFile.name);
    })
</script>
</body>
</html>

补充:有用xhr做过图片直传七牛。用canvas压缩了图片,该方法还是不兼容IE8,是用appcan做一个小项目的时候用过。附上代码。(需求是多图片上传,需要一张一张上传,并且如果有其中一张图片在上传时失败,则停止上传剩下的图片,如果全部上传成功,则提交整个表单,submitDone()方法就是提交表单,因为与本文没什么关系,就不写出来了。)

function putb64(dataArr) {
    var pic = dataArr[isSuc].substring(23); // pic是图片的base64编码
    var timestamp = (new Date()).valueOf();
    var url = "http://upload.qiniu.com/putb64/-1";
    var xhr = new XMLHttpRequest();
    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-Type", "application/octet-stream");
    xhr.setRequestHeader("Authorization", "UpToken " + $("input[name=token]").val());
    xhr.send(pic);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
                var data = $.parseJSON(xhr.responseText);
                key.push(data.key); ++isSuc;
                if (isSuc < baseArr.length) {
                    putb64(dataArr);
                } else if (isSuc == baseArr.length) {
                    submitDone();
                }

            } else {

                appcan.window.alert({
                    title : '',
                    content : xhr.responseText,
                    buttons : '确定'
                });
                stopLoading();
                toast('图片上传失败');
            }
        }
    }
}

附上图片转base64编码方法

var img = new Image();
img.src = imgArr[imgIndex]; //imgArr是图片数组
img.onload = function() {
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;

    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);

    var dataURL = canvas.toDataURL("image/jpeg", 0.4); // 0.4就是图片压缩的程度
    baseArr.push(dataURL);
}

ermaoL
32 声望3 粉丝

引用和评论

0 条评论