Ajax 文件上傳

常見異步上傳方案


  1. 使用第三方控件(Flash,ActiveX, 瀏覽器插件等)

優(yōu)點(diǎn):

  • 交互與可控性好(多文件、進(jìn)度展示、續(xù)傳、暫停)
  • 性能好(可使用底層協(xié)議通信)

缺點(diǎn):
+ 需要瀏覽器安裝插件

  1. 使用隱藏的iframe模擬異步上傳

優(yōu)點(diǎn):

  • 瀏覽器原生支持,不需要插件
  • 廣泛的瀏覽器兼容性

缺點(diǎn):

  • 交互差,體驗(yàn)差,上傳過程基本不可控
  • 性能差

關(guān)鍵技術(shù)點(diǎn):

  1. form指定target,提交結(jié)果定向到隱藏的iframe。
  2. 提交完成后,iframe中頁面與主頁面通信,通知上傳結(jié)果及服務(wù)端文件信息。

html

    <form action="/upload2" enctype="multipart/form-data" method="post" target="frm" onsubmit="loading(true);">
      <p id="upfile">
        附件: <input type="file" name="myfile" style="display: inline">
      </p>
      <p id="upbtn">
        <input class="btn btn-primary btn-sm" style="padding-left:50px;padding-right: 50px;" type="submit" value="異步上傳">
        <span id="uptxt" style="display: none">正在上傳...</span>
      </p>
    </form>

    <div id="flist" style="border:1px dotted darkgray;"></div>

script

    // 上傳完成后的回調(diào)
    function uploadFinished(fileName) {
        addToFlist(fileName);
        loading(false);
    }

    function addToFlist(fname) {
        var temp = ["<p id='" + fname + "'>",
                    fname,
            "<button onclick='delFile(\"" + fname + "\");'>刪除</button>",
                    "</p>"
        ];
        $("#flist").append(temp.join(""));
    }

    function delFile(fname) {
        console.log('to delete file: ' + fname);
        // TODO: 請實(shí)現(xiàn)
    }

    function loading(showloading) {
        if (showloading) {
            $("#uptxt").show();
        } else {
            $("#uptxt").hide();
        }
    }

node.js

    var express = require('express');
    var router = express.Router();

    var multipart = require('connect-multiparty');
    var multipartMiddleware = multipart();

    var fs = require('fs');

    router.all('/', function (req, res) {
        res.sendFile('../public/index.html');
    });

    router.post('/upload2', multipartMiddleware, function(req, res) {
        console.log(req.body);
        console.log(req.files);

        // 實(shí)際編程時,一般要將臨時文件移動到目標(biāo)位置,之后刪除臨時文件
        // 課程中為簡化操作,直接將臨時文件當(dāng)成目標(biāo)文件
        var fpath = req.files.myfile.path;
        var fname = fpath.substr(fpath.lastIndexOf('\\') + 1);

        setTimeout(function() {
            var ret = ["<script>",
                "window.parent.uploadFinished('" + fname + "');",
                "</script>"];
            res.send(ret.join(""));
        }, 3000);

    });

    module.exports = router;
  1. 使用xhr level 2 純ajax異步上傳

優(yōu)點(diǎn):

  • 支持H5的瀏覽器原生支持,不需要插件
  • 交互性較好

缺點(diǎn):

  • 受瀏覽器支持限制

關(guān)鍵過程:

  1. 創(chuàng)建FormData,放入待上傳文件

  2. 通過xhr操作將FormData發(fā)送到服務(wù)器,實(shí)現(xiàn)文件上傳

  3. 綁定progress、load、error等事件監(jiān)聽傳輸過程并在頁面顯示動態(tài)交互信息

html

    <div>
        <p id="upfile">附件: <input type="file" id="myfile" style="display: inline"></p>
        <p id="upbtn">
            <input class="btn btn-primary btn-sm" style="padding-left:50px;padding-right: 50px;" type="button" value="異步上傳" onclick="upload();">
            <span id="uptxt" style="display: none">正在上傳...</span>
            <span id="upprog"></span>
            <button id="stopbtn" style="display:none;">停止上傳</button>
        </p>
    </div>

    <div id="flist" style="border:1px dotted darkgray;"></div>

javascript

    function upload() {
        // 1.準(zhǔn)備FormData
        var fd = new FormData();
        fd.append("myfile", $("#myfile")[0].files[0]);

        // 創(chuàng)建xhr對象
        var xhr = new XMLHttpRequest();

        // 監(jiān)聽狀態(tài),實(shí)時響應(yīng)
        // xhr 和 xhr.upload 都有progress事件,xhr.progress是下載進(jìn)度,xhr.upload.progress是上傳進(jìn)度
        xhr.upload.onprogress = function(event) {
            if (event.lengthComputable) {
                var percent = Math.round(event.loaded * 100 / event.total);
                console.log('%d%', percent);
                $("#upprog").text(percent);
            }
        };

        // 傳輸開始事件
        xhr.onloadstart = function(event) {
            console.log('load start');
            $("#upprog").text('開始上傳');

            $("#stopbtn").one('click', function() {
               xhr.abort();
                $(this).hide();
            });

            loading(true);
        };

        // ajax過程成功完成事件
        xhr.onload = function(event) {
            console.log('load success');
            $("#upprog").text('上傳成功');

            console.log(xhr.responseText);
            var ret = JSON.parse(xhr.responseText);
            addToFlist(ret.fname);
        };

        // ajax過程發(fā)生錯誤事件
        xhr.onerror = function(event) {
            console.log('error');
            $("#upprog").text('發(fā)生錯誤');
        };

        // ajax被取消
        xhr.onabort = function(event) {
            console.log('abort');
            $("#upprog").text('操作被取消');
        };

        // loadend傳輸結(jié)束,不管成功失敗都會被觸發(fā)
        xhr.onloadend = function (event) {
            console.log('load end');
            loading(false);
        };

        // 發(fā)起ajax請求傳送數(shù)據(jù)
        xhr.open('POST', '/upload3', true);
        xhr.send(fd);
    }

    function addToFlist(fname) {
        var temp = ["<p id='" + fname + "'>",
                    fname,
            "<button onclick='delFile(\"" + fname + "\");'>刪除</button>",
                    "</p>"
                    ];
        $("#flist").append(temp.join(""));
    }

    function delFile(fname) {
        console.log('to delete file: ' + fname);
        // TODO: 請實(shí)現(xiàn)
    }

    function loading(showloading) {
        if (showloading) {
            $("#uptxt").show();
            $("#stopbtn").show();
        } else {
            $("#uptxt").hide();
            $("#stopbtn").hide();
        }
    }

node.js

    var express = require('express');
    var router = express.Router();

    var multipart = require('connect-multiparty');
    var multipartMiddleware = multipart();

    var fs = require('fs');

    router.all('/', function (req, res) {
        res.sendFile('../public/index.html');
    });

    router.post('/upload3', multipartMiddleware, function(req, res) {
        console.log(req.body);
        console.log(req.files);

        // 實(shí)際編程時,一般要將臨時文件移動到目標(biāo)位置,之后刪除臨時文件
        // 課程中為簡化操作,直接將臨時文件當(dāng)成目標(biāo)文件
        var fpath = req.files.myfile.path;
        var fname = fpath.substr(fpath.lastIndexOf('\\') + 1);

        res.json({fname: fname});
    });

    module.exports = router;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容