2014年3月9日 星期日

HTML5檔案上傳進度條


要掌握上傳進度有一個關鍵: Client Script必須掌握檔案大小以及已上傳資料量,才可能計算上傳百分比回報狀態。傳統使用<input type="file">選取檔案配合<input type="submit">送出鈕的做法,一來無法得知所選取檔案大小,二來在按下POST鈕後Script即失去主導權,一切交由瀏覽器主控, 更別奢談得知上傳進度。在從桌面拖拉檔案到網頁一 文提到的HTML5 File API,一舉突破JavaScript無從得知檔案大小的盲點,邁進一大步。而透過XHR(XMLHttpRequest),改用 jQuery.ajax()非同步上傳檔案,便能在上傳過程繼續更新網頁回報進度。這樣子只剩下一個挑戰 -- 如何得知已上傳資料量?

好消息! 隨著瀏覽器日新月異,XHR也跟著進化,HTML5世代瀏覽器(IE需為IE10+,IE9又哭哭了...)已內建XMLHttpRequest Level 2(XHR2),增加不少新功能,包含直接處理ArrayBuffer/Blob等二進位資料的能力,也多了onprogress事件,能在傳輸過程中持續觸發回報上傳進度! 有了新武器,要實現上傳進度回報就簡單多了。


原始介紹文章出自於此


@{
    Layout = null;
}
<!DOCTYPE html>
 
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Ajax Upload Lab</title>
    <style>
        .item {
            background-color: #6699CC; border: 1px solid gray;
            font-family: 'Courier New'; font-size: 8.5pt;
            margin-bottom: 6px; padding: 3px; color: yellow;
            box-shadow: 3px 3px 3px 1px rgba(128, 128, 128, 0.7);
        }
            .item .name { text-shadow: 1px 1px gray;  }
 
        .prg-zone { margin-top: 10px; }
 
        .bar {
            background-color: #666666;
            height: 15px; position: relative;
            margin: 3px; margin-top: 6px;
            margin-bottom: 6px; border: 1px solid #ccc;
            border-top-color: #444; border-left-color: #444;
        }
            .bar > div {
                position: absolute; color: white; font-size: 8pt;
            }
            .bar .color-bar {
                background-color: #99bb33; top: 0px; bottom: 0px;
                left: 0px;
            }
            .bar .status { top: 0px; left: 6px; }
            .bar .progress { top: 0px; right: 4px; }
    </style>
</head>
<body>
    <div>
        <input type="file" id="fileSelector" multiple
               data-bind="event: { change: selectorChange }" />
        <input type="button" value="Upload" data-bind="click: upload" />
    </div>
    <div data-bind="foreach: files" class="prg-zone">
        <div class="item">
            <div data-bind="text: name" class="name"></div>
            <div class="bar">
                <div class="color-bar" data-bind="attr: { 'style': widthStyle }"></div>
                <div class="status" data-bind="text: status"></div>
                <div class="progress" data-bind="text: progress"></div>
            </div>
        </div>
    </div>
    <script src="~/Scripts/jquery-2.1.0.js"></script>
    <script src="~/Scripts/knockout-3.1.0.debug.js"></script>
    <script>
        $(function () {
 
            function viewModel() {
                var self = this;
                self.files = ko.observableArray();
                self.selectorChange = function (item, e) {
                    self.files.removeAll();
                    $.each(e.target.files, function (i, file) {
                        //加入額外屬性
                        file.uploadedBytes = ko.observable(0); //已上傳Bytes
                        file.percentage = ko.computed(function () { //上傳百分比
                            return (file.uploadedBytes() * 100 / file.size).toFixed(1);
                        });
                        file.widthStyle = ko.computed(function () {
                            return "right:" + (100 - file.percentage()) + "%";
                        });
                        //上傳進度數字顯示
                        file.progress = ko.computed(function () {
                            var perc = file.percentage();
                            return file.uploadedBytes.peek() + "/" + file.size +
                                "(" + perc + "%)";
                        });
                        file.message = ko.observable();
                        file.status = ko.computed(function () {
                            var msg = file.message(), perc = file.percentage();
                            if (msg) return msg;
                            if (perc == 0) return "Waiting";
                            else if (perc == 100) return "Done";
                            else return "Uploading...";
                        });
                        self.files.push(file);
                    });
                };
 
                self.upload = function () {
                    $.each(self.files(), function (i, file) {
                        var reader = new FileReader();
                        reader.onload = function (e) {
                            var data = e.target.result;
                            //https://gist.github.com/HenrikJoreteg/2502497
                            //以XHR上傳原始格式
                            $.ajax({
                                type: "POST",
                                url: "@Url.Content("~/xhr2/upload")" + "?file=" + file.name,
                                contentType: "application/octect-stream",
                                processData: false, //不做任何處理,只上傳原始資料
                                data: data,
                                xhr: function () {
                                    //建立XHR時,加掛onprogress事件
                                    var xhr = $.ajaxSettings.xhr();
                                    xhr.upload.onprogress = function (evt) {
                                        file.uploadedBytes(evt.loaded);
                                    };
                                    return xhr;
                                }
                            });
                        };
                        reader.readAsArrayBuffer(file);
                    });
                };
            }
            var vm = new viewModel();
            ko.applyBindings(vm);
 
        });
    </script>
</body>
</html>

沒有留言:

張貼留言