文件上傳的幾種方法——xhr篇

在以往的項目,用過三種方式的文件上傳,分別是xhr、webuploader、plupload,好記性不如爛筆頭,在開始整理筆記的時候,文件上傳的優先級就排得敲級高,畢竟特別經常使用嘛~表達能力有限,只能無限開門放代碼。。。css


xhr

這個方法的兼容性不太好。html

XMLHttpRequest一開始只是微軟瀏覽器提供的一個接口,後來各大瀏覽器紛紛效仿也提供了這個接口,再後來W3C對它進行了標準化,提出了XMLHttpRequest標準。XMLHttpRequest標準又分爲Level 1和Level 2。
XMLHttpRequest Level 1主要存在如下缺點:
受同源策略的限制,不能發送跨域請求;
不能發送二進制文件(如圖片、視頻、音頻等),只能發送純文本數據;
在發送和獲取數據的過程當中,沒法實時獲取進度信息,只能判斷是否完成;jquery

Level 2對Level 1 進行了改進,XMLHttpRequest Level 2中新增瞭如下功能:
能夠發送跨域請求,在服務端容許的狀況下;
支持發送和接收二進制數據;
新增formData對象,支持發送表單數據;
發送和獲取數據時,能夠獲取進度信息;
能夠設置請求的超時時間;web

圖片是XmlHttpRequest Level 2的兼容性
圖片描述json

從圖中能夠看到:canvas

IE8/IE九、Opera Mini 徹底不支持xhr對象
IE10/IE11部分支持,不支持 xhr.responseType爲json
部分瀏覽器不支持設置請求超時,即沒法使用xhr.timeout
部分瀏覽器不支持xhr.responseType爲blobsegmentfault

以上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);
}
相關文章
相關標籤/搜索