关于使用ajax上传文件之ajaxfileupload.js源码剖析

    或许对于ajax和文件上传大家都很熟悉,但是大家是否有想过使用ajax该如何上传。当然这在实际项目又将是比较常见的。所以,我在这里也简单的分享下如何用ajaxfileupload.js做文件上传,以及ajaxfileupload.js上传的原理是什么。当然相信大家定有更好的实现方式,希望能共同分享。

    首先我们得下载ajaxfileupload.js 下载地址:http://sdrv.ms/PO4elz

     当然接下来将是把这个js引入我们的项目中。

     页面代码如下:

  

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>图片上传页面</title>

<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
<script type="text/javascript" src="${pageContext.request.contextPath}/common/javascripts/jquery-1.6.2.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/common/javascripts/ajaxfileupload.js"></script>
<script type="text/javascript">

function add(x,y){
$.ajaxFileUpload({
url:'./shop/goodsUpload.action?rad='+Math.random(), //你处理上传文件的服务端
secureuri:false,
fileElementId:y,
cache:false,
dataType: 'text/html',
success: function (data){
$("#"+x+"").html("<img style='width: 88px;height: 88px;' alt='' src='"+data+"'>");
}
}
)
return false;
}

function show(x){
$("#"+x+"_x").click();
}

</script>
<style type="text/css">
font{
font-size: 15px;
}
</style>
</head>

<body>


<div style="margin-top: 30px;height: 800px;">
<h3>为宝贝上传图片</h3>
<br>
<hr>
<table>
<tr>
<td>
<table id="image1">
<tr>
<td>
<input type="hidden" value="<s:property value="id"/>" name="goodsId">
<div style="width: 90px;height: 90px;border: #E6E6E6 1px solid;" id="1"></div>
</td>
</tr>
<tr>
<td><input style="width: 60px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');display: none;" type="file" id="image1_x" name="image" onchange="add(1,'image1')">
<input type="button" style="width: 89px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');height:27px;" onclick="show('image1')">
</td>
</tr>

</table>
</td>
<td>
<table id="image2">

<tr>
<td>
<input type="hidden" value="<s:property value="id"/>" name="goodsId">
<div style="width: 90px;height: 90px;border: #E6E6E6 1px solid;" id="2"></div>
</td>
</tr>
<tr>
<td><input style="width: 60px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');display: none;" type="file" id="image2_x" name="image" onchange="add(2,'image2')">
<input type="button" style="width: 89px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');height:27px;" onclick="show('image2')">
</td>
</tr>

</table>
</td>
<td>
<table id="image3">
<tr>
<td>
<input type="hidden" value="<s:property value="id"/>" name="goodsId">
<div style="width: 90px;height: 90px;border: #E6E6E6 1px solid;" id="3"></div>
</td>
</tr>
<tr>
<td><input style="width: 60px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');display: none;" type="file" id="image3_x" name="image" onchange="add(3,'image3')">
<input type="button" style="width: 89px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');height:27px;" onclick="show('image3')">
</td>
</tr>

</table>
</td>

<td>
<table id="image4">
<tr>
<td>
<input type="hidden" value="<s:property value="id"/>" name="goodsId">
<div style="width: 90px;height: 90px;border: #E6E6E6 1px solid;" id="4"></div>
</td>
</tr>
<tr>
<td><input style="width: 60px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');display: none;" type="file" id="image4_x" name="image" onchange="add(4,'image4')">
<input type="button" style="width: 89px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');height:27px;" onclick="show('image4')">
</td>
</tr>

</table>
</td>
<td>
<table id="image5">
<tr>
<td>
<input type="hidden" value="<s:property value="id"/>" name="goodsId">
<div style="width: 90px;height: 90px;border: #E6E6E6 1px solid;" id="5"></div>
</td>
</tr>
<tr>
<td><input style="width: 60px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');display: none;" type="file" id="image5_x" name="image" onchange="add(5,'image5')">
<input type="button" style="width: 89px;background-image: url('${pageContext.request.contextPath }/shop/images/shops/upload.png');height:27px;" onclick="show('image5')">
</td>
</tr>

</table>
</td>

</tr>
</table>
</div>
</body>
</html>

这样就完成了页面的工作,当然后台方法就和普通上传没有什么区别了,所以这里就不贴后台方法了。这样只要每次你选择图片之后,add方法中的$.ajaxFileUpload就会自动向服务器发起请求并调用你的后台方法将图片上传到你指定的路径中。

 

     在这里我们需要思考的是,我们直接用ajax,而不用ajaxFileUpload.js不行么,当然,答案是肯定的,但是我们需要做的是想办法你获取的文件以二进制的形式传输到服务器。

     如果闲那样麻烦,那就只能和我一样选择一个插件。当然选用了插件之后我们定会在空闲的时候对他的源码产生兴趣,所以我们现在就来剖析一下ajaxFileUpload.js

     ajaxFileUpload.js 源码很少,所以研究起来并不困难。

     源码如下:

    

// JavaScript Document
jQuery.extend({

createUploadIframe: function(id, uri)
{
//create frame
var frameId = 'jUploadFrame' + id;

if(window.ActiveXObject) {
var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
if(typeof uri== 'boolean'){
io.src = 'javascript:false';
}
else if(typeof uri== 'string'){
io.src = uri;
}
}
else {
var io = document.createElement('iframe');
io.id = frameId;
io.name = frameId;
}
io.style.position = 'absolute';
io.style.top = '-1000px';
io.style.left = '-1000px';

document.body.appendChild(io);

return io;
},
createUploadForm: function(id, fileElementId)
{
//create form
var formId = 'jUploadForm' + id;
var fileId = 'jUploadFile' + id;
var form = jQuery('<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
var oldElement = jQuery('#' + fileElementId);
var newElement = jQuery(oldElement).clone();
jQuery(oldElement).attr('id', fileId);
jQuery(oldElement).before(newElement);
jQuery(oldElement).appendTo(form);
//set attributes
jQuery(form).css('position', 'absolute');
jQuery(form).css('top', '-1200px');
jQuery(form).css('left', '-1200px');
jQuery(form).appendTo('body');
return form;
},

ajaxFileUpload: function(s) {
// TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
s = jQuery.extend({}, jQuery.ajaxSettings, s);
var id = s.fileElementId;
var form = jQuery.createUploadForm(id, s.fileElementId);
var io = jQuery.createUploadIframe(id, s.secureuri);
var frameId = 'jUploadFrame' + id;
var formId = 'jUploadForm' + id;

if( s.global && ! jQuery.active++ )
{
// Watch for a new set of requests
jQuery.event.trigger( "ajaxStart" );
}
var requestDone = false;
// Create the request object
var xml = {};
if( s.global )
{
jQuery.event.trigger("ajaxSend", [xml, s]);
}

var uploadCallback = function(isTimeout)
{
// Wait for a response to come back
var io = document.getElementById(frameId);
try
{
if(io.contentWindow)
{
xml.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;
xml.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;

}else if(io.contentDocument)
{
xml.responseText = io.contentDocument.document.body?io.contentDocument.document.body.innerHTML:null;
xml.responseXML = io.contentDocument.document.XMLDocument?io.contentDocument.document.XMLDocument:io.contentDocument.document;
}
}catch(e)
{
jQuery.handleError(s, xml, null, e);
}
if( xml || isTimeout == "timeout")
{
requestDone = true;
var status;
try {
status = isTimeout != "timeout" ? "success" : "error";
// Make sure that the request was successful or notmodified
if( status != "error" )
{
// process the data (runs the xml through httpData regardless of callback)
var data = jQuery.uploadHttpData( xml, s.dataType );
if( s.success )
{
// ifa local callback was specified, fire it and pass it the data
s.success( data, status );
};
if( s.global )
{
// Fire the global callback
jQuery.event.trigger( "ajaxSuccess", [xml, s] );
};

jQuery.removeData(data);
} else
{
jQuery.handleError(s, xml, status);
}

} catch(e)
{
status = "error";
jQuery.handleError(s, xml, status, e);
};
if( s.global )
{
// The request was completed
jQuery.event.trigger( "ajaxComplete", [xml, s] );
};

// Handle the global AJAX counter
if(s.global && ! --jQuery.active)
{
jQuery.event.trigger("ajaxStop");
};
if(s.complete)
{
s.complete(xml, status);
} ;

jQuery(io).unbind();

setTimeout(function()
{ try
{
jQuery(io).remove();
jQuery(form).remove();

} catch(e)
{
jQuery.handleError(s, xml, null, e);
}

}, 100);

xml = null;

};
}
// Timeout checker
if( s.timeout > 0 )
{
setTimeout(function(){

if( !requestDone )
{
// Check to see ifthe request is still happening
uploadCallback( "timeout" );
}

}, s.timeout);
}
try
{
var form = jQuery('#' + formId);
jQuery(form).attr('action', s.url);
jQuery(form).attr('method', 'POST');
jQuery(form).attr('target', frameId);
if(form.encoding)
{
form.encoding = 'multipart/form-data';
}
else
{
form.enctype = 'multipart/form-data';
}
jQuery(form).submit();

} catch(e)
{
jQuery.handleError(s, xml, null, e);
}
if(window.attachEvent){
document.getElementById(frameId).attachEvent('onload', uploadCallback);
}
else{
document.getElementById(frameId).addEventListener('load', uploadCallback, false);
}
jQuery.removeData(s);
return {abort: function () {}};

},

uploadHttpData: function( r, type ) {
var data = !type;
data = type == "xml" || data ? r.responseXML : r.responseText;
// ifthe type is "script", eval it in global context
if( type == "script" )
{
jQuery.globalEval( data );
}

// Get the JavaScript object, ifJSON is used.
if( type == "json" )
{
eval( "data = " + data );
}

// evaluate scripts within html
if( type == "html" )
{
jQuery("<div>").html(data).evalScripts();
}

return data;
}
});

在这源文件中我们可以很清晰的看到,其实ajaxfileupload并没有什么神奇的地方,因为它其实也并没有把文件转换成二进制。他首先是在页面中创建一个iframe,然后再创建一个form表单,并将fileElementId所指定的区域一并拼接到form表单中。当你触发ajax事件的时候,它会自动将这个表单提交。这就是ajax整个工作原理。当然,有时你还会在上传文件的时候向后台传其它必要的参数,但是这个js貌似没有提供data属性。那怎么办呢,我所想到的方法有两个,方法一是重写ajaxfileupload.js。当然这并不麻烦,源码很清晰,并且并不多。但是我选择了一个更简单的方法,只是显得有些不够优雅,但是我觉得还是可行的,那就是方法二,直接在fileElementId指定的区域中放隐藏字段。因为ajaxfileupload.js是将整个fileelementid所指定的区域都拼接到了表单中,所以提交的时候,只要是在fileElementId所包含的范围内的所有有name的文本域都将会被提交到后台。这样便可实现传参。只是这样没有jquery.ajax那样优雅而已。

 

这是笔者自己的一点小经验,如果有更好的方案,或改进的地方希望能与您共同分享,共同进步。

posted on 2012-08-12 18:30  JulianTu  阅读(1383)  评论(0)    收藏  举报

导航