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