Stay Hungry,Stay Foolish!

AjaxFileUpload 方法与原理分析

AjaxFileUpload需求

传统的form表单方式上传文件,  必然会刷新整个页面。 那么在不刷新界面的情况下实现文件的上传呢?

在 HTML4下, 聪明的程序员们发明了 ajax file upload 方式(form + hidden iframe方式), 为本文介绍的对象。

 在HTML5中XMLHttpRequest实现了异步上传文件(介绍见 http://wenzhixin.net.cn/2013/11/28/ajax_file_upload_html5)。

 

开源项目

官网为 https://github.com/davgothic/AjaxFileUpload

此插件支持, 选择文件后,  立刻上传文件, 整体页面不刷新,  并可以根据后台返回的json报给用户上传的状况。

 

使用说明

此插件是基于jquery库,  需要引用jquery库,

    <script type="text/javascript" src="jquery-1.6.1.min.js"></script>
    <script type="text/javascript" src="../jquery.ajaxfileupload.js"></script>

 

文件控件(使用此插件 form都不用  写):

    <form method="post" action="" enctype="multipart/form-data">
        <label>File Input: <input type="file" name="file" id="demo1" /></label>
        <div id="uploads">

        </div>
    </form>

使用此插件初始化控件:

    <script type="text/javascript">
        $(document).ready(function() {
            $("#demo1").AjaxFileUpload({
                onComplete: function(filename, response) {
                    $("#uploads").append(
                        $("<img />").attr("src", filename).attr("width", 200)
                    );
                }
            });
        });
    </script>

 

后台实现PHP参考:

<?php
/**
 * This is just an example of how a file could be processed from the
 * upload script. It should be tailored to your own requirements.
 */
// Only accept files with these extensions
$whitelist = array('jpg', 'jpeg', 'png', 'gif');
$name      = null;
$error     = 'No file uploaded.';
if (isset($_FILES)) {
    if (isset($_FILES['file'])) {
        $tmp_name = $_FILES['file']['tmp_name'];
        $name     = basename($_FILES['file']['name']);
        $error    = $_FILES['file']['error'];
        
        if ($error === UPLOAD_ERR_OK) {
            $extension = pathinfo($name, PATHINFO_EXTENSION);
            if (!in_array($extension, $whitelist)) {
                $error = 'Invalid file type uploaded.';
            } else {
                move_uploaded_file($tmp_name, $name);
            }
        }
    }
}
echo json_encode(array(
    'name'  => $name,
    'error' => $error,
));
die();

 

定制接口

四个定制接口, 可以定制内容包括:

1 上传url (定制自定义的 action)

2  提交submit事件 (例如对文件后缀校验不成功, 不上传)

3  选中文件change事件 (定制选中文件后的 等待进度条等)

4 提交完成后的响应事件 complete (提交成功后, 将提交结果添加到列表中)

    <form method="post" action="" enctype="multipart/form-data">
        <label>File Input: <input type="file" name="file" id="demo1" /></label>
    </form>
    <script type="text/javascript" src="jquery-1.6.1.min.js"></script>
    <script type="text/javascript" src="../jquery.ajaxfileupload.js"></script>
    <script type="text/javascript">
        $(document).ready(function() {
            var interval;
            function applyAjaxFileUpload(element) {
                $(element).AjaxFileUpload({
                    action: "upload.php",
                    onChange: function(filename) {
                        // Create a span element to notify the user of an upload in progress
                        var $span = $("<span />")
                            .attr("class", $(this).attr("id"))
                            .text("Uploading")
                            .insertAfter($(this));
                        $(this).remove();
                        interval = window.setInterval(function() {
                            var text = $span.text();
                            if (text.length < 13) {
                                $span.text(text + ".");
                            } else {
                                $span.text("Uploading");
                            }
                        }, 200);
                    },
                    onSubmit: function(filename) {
                        // Return false here to cancel the upload
                        /*var $fileInput = $("<input />")
                            .attr({
                                type: "file",
                                name: $(this).attr("name"),
                                id: $(this).attr("id")
                            });
                        $("span." + $(this).attr("id")).replaceWith($fileInput);
                        applyAjaxFileUpload($fileInput);
                        return false;*/
                        // Return key-value pair to be sent along with the file
                        return true;
                    },
                    onComplete: function(filename, response) {
                        window.clearInterval(interval);
                        var $span = $("span." + $(this).attr("id")).text(filename + " "),
                            $fileInput = $("<input />")
                                .attr({
                                    type: "file",
                                    name: $(this).attr("name"),
                                    id: $(this).attr("id")
                                });
                        if (typeof(response.error) === "string") {
                            $span.replaceWith($fileInput);
                            applyAjaxFileUpload($fileInput);
                            alert(response.error);
                            return;
                        }
                        $("<a />")
                            .attr("href", "#")
                            .text("x")
                            .bind("click", function(e) {
                                $span.replaceWith($fileInput);
                                applyAjaxFileUpload($fileInput);
                            })
                            .appendTo($span);
                    }
                });
            }
            applyAjaxFileUpload("#demo1");
        });
    </script>

 

实现原理

参考下面文章的说明:

http://wenzhixin.net.cn/2013/11/27/ajax_file_upload_iframe

 

此处结合此开源项目的代码介绍下:

1、 应用 AjaxFileUpload 到 file控件后, 此file控件就绑定了 change事件, 事件函数为 onChange

        return this.each(function() {
            var $this = $(this);
            if ($this.is("input") && $this.attr("type") === "file") {
                $this.bind("change", onChange);
            }
        });

2、 用户选择文件后, 触发 change事件, 调用 onChange 函数

     此函数, 先克隆一个 file控件, 并将 此克隆控件,  也使用 AjaxFileUpload  初始化(即绑定了 onChange函数,地位与原始控件相同了),

     并将此控件 插入到 原始控件前。

                $clone   = $element.removeAttr('id').clone().attr('id', id).AjaxFileUpload(options),
。。。。。
            // We append a clone since the original input will be destroyed
            $clone.insertBefore($element);

     后创建  隐藏的 iframe, 并初始化 iframe的 load事件(form提交后响应的框架), 事件函数即为 响应处理函数:

                iframe   = createIframe(),
。。。。

            iframe.bind("load", {element: $clone, form: form, filename: filename}, onComplete);

     后创建提交的form, 将form的提交事件绑定为onSubmit, 并做提交动作:

                form     = createForm(iframe);
。。。。。
            form.append($element).bind("submit", {element: $clone, iframe: iframe, filename: filename}, onSubmit).submit();

 

3、 2步骤中form提交后, 文件上传成功, 在iframe的load事件被触发, 执行onComplete函数, 将执行结果进行处理, 处理完毕后将 onChange中创建的 form 和 iframe删除:

        function onComplete (e) {
            var $iframe  = $(e.target),
                doc      = ($iframe[0].contentWindow || $iframe[0].contentDocument).document,
                response = doc.body.innerHTML;

            if (response) {
                response = $.parseJSON(response);
            } else {
                response = {};
            }
            // 自定义的处理逻辑
            settings.onComplete.call(e.data.element, e.data.filename, response);
            
            // Remove the temporary form and iframe
            e.data.form.remove();
            $iframe.remove();
        }

 

 

change事件只生效一次说明

如果对 file 控件应用 ajaxfileupload 控件初始化后, 接着设置 一个附加的 change 事件, 在第一用户选择文件上传后, 会丢失, 是因为 文件控件被替换为clone体, 但是clone体没带有old event, 生效代码为 clone参数为空, 意味着浅拷贝:

        function onChange(e) {
            var $element = $(e.target),
                id       = $element.attr('id'),
                $clone   = $element.removeAttr('id').clone().attr('id', id).AjaxFileUpload(options),
                filename = $element.val().replace(/.*(\/|\\)/, ""),
                iframe   = createIframe(),
                form     = createForm(iframe);

            // We append a clone since the original input will be destroyed
            $clone.insertBefore($element);

 

解决方法,使用代理:

$(document).on('change', '#upload', function() {

});

参考:

http://wenzhixin.net.cn/2013/11/26/jquery_file_upload_change_once

 

posted @ 2015-07-07 01:04  lightsong  阅读(5229)  评论(1编辑  收藏  举报
Life Is Short, We Need Ship To Travel