http://www.jb51.net/article/51547.htm
无论是PHP,还是其他的服务端脚本都提供了文件上传功能,实现起来也比较简单。而利用JavaScript来配合,即可实现Ajax方式的文件上传。虽然jQuery本身没有提供这样的简化函数,但有不少插件可以实现。其中,Phpletter.com提供的ajaxfileupload.js是一个轻量的插件,而且编写方式与jQuery提供的全局方法$.post()非常相似,简单易用。 不过,该插件实在太简化了,除了可提供需上传文件的路径外,也就不能传递额外的值到后台服务端。所以,我修改了一下该脚本,增加个一个data对象参数。
一、原理
我这里使用的是PHP作为服务端脚本,几乎在每本较少PHP的书上都会提到如何使用move_uploaded_file()方法来上传文件,这里我就不再细说了。我想说的是,利用Ajax上传的原理。 因为一直在使用jQuery库,所以当想到Ajax时,第一反应就是试试$.post()方法,利用各选择器得到file文件框中的value值,然后提交到后台服务端。当然,后来证明这是不行的。(正因为这问题,我还查了不少资料,网上还提供了不少ASP等方式的脚本,真不知道该说什么好。。) 回到正题,要实现Ajax方式上传,其实并不难,方法也有不少。而本文提到的Phpletter.com的ajaxfileupload.js插件就是使用iframe的方式。这也是在不使用JavaScript脚本时,要实现不刷新页面上传时常见的方法。(本博客bo-blog后台撰写日志就是用该方法) 而ajaxfileupload.js插件也很简单,就是先利用jQuery的选择器获得file文件上传框中的文件路径值,然后动态的创建一个iframe,并在里面建立一个新的file 文件框,提供post方式提交到后台。最后,返回结果到前台。
二、使用
ajaxfileupload.js插件的使用很简单。 前台HTML代码类似:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<script type= "text/javascript" >
$( #buttonUplod).click(function () {
$.ajaxFileUpload ({
url: 'doajaxfileupload.php' , //你处理上传文件的服务端
secureuri: false , //与页面处理代码中file相对应的ID值
fileElementId: 'img' ,
dataType: 'json' , //返回数据类型:text,xml,json,html,scritp,jsonp五种
success: function (data) {
alert(data.file_infor);
}
})
}); </script> <input id= "img" type= "file" size= "45" name= "img" >
<button id= "buttonUpload" onclick= "return ajaxFileUpload();" >Upload</button>
|
后台doajaxfileupload.php脚本:
1
2
3
4
5
6
7
8
9
|
<?php $upFilePath = "../attachment/" ;
$ok =@move_uploaded_file( $_FILES [ 'img' ][ 'tmp_name' ], $upFilePath );
if ( $ok === FALSE){
echo json_encode( 'file_infor' => '上传失败' );
} else {
echo json_encode( 'file_infor' => '上传成功' );
}
?> |
为了测试,可以使用类似下面的方式保存传递过来的变量值:
$file_info = var_export($_FILES,true); $ok = file_put_contents("../attachment/file_info.txt",$file_info); if ($ok) exit(json_encode('file_infor'=>'上传成功')); exit (json_encode('file_infor'=>'上传失败'));
※ 注意 请留意HTML代码文件框中的标记:
1. id='img'是用于给ajaxfileupload.js插件的fileElementId:'img'识别的,jQuery选择器会利用该字符串获得文本框的值; 2. name='img'是用于通过post方式提交到后台脚本时,PHP通过$_FILES['img']读取上传文件的数据,若没有该值,$_FILES变量为空;
所以,这两个值缺一不可,也不可混淆。
三、支持额外参数
有时候,我们需要在后台根据某些变量来觉得对上传文件的处理。例如,更新文件。这时,就需要往同台再传递一些额外的参数。所以,我修改了ajaxfileupload.js插件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
addOtherRequestsToForm: function (form,data)
{ // add extra parameter
var originalElement = $( '<input type="hidden" name="" value="">' );
for ( var key in data) {
name = key;
value = data[key];
var cloneElement = originalElement.clone();
cloneElement.attr({ 'name' :name, 'value' :value});
$(cloneElement).appendTo(form);
}
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 = new Date().getTime()
var form = jQuery.createUploadForm(id, s.fileElementId);
if ( s.data ) form = jQuery.addOtherRequestsToForm(form,s.data);
var io = jQuery.createUploadIframe(id, s.secureuri);
|
红色标记部分是我添加的内容。这样,我就可以在前台HTML部分,通过类似下面的代码来传递额外的参数:
url:'doajaxfileupload.php', //你处理上传文件的服务端 secureuri:false, //与页面处理代码中file相对应的ID值 data:{'test':'test','ok':'ok'}, //以对象的方式传递,内容部分可输入JavaScript的变量值 fileElementId:'img',
后台处理脚本为:
1
2
3
4
5
|
array_push ( $_FILES , $_REQUEST );
$file_info = var_export( $_FILES ,true);
$ok = file_put_contents ( "../attachment/file_info.txt" , $file_info );
if ( $ok ) exit (json_encode( 'file_infor' => '上传成功' ));
exit (json_encode( 'file_infor' => '上传失败' ));
|
可见,原理很简单,就是把额外的data对象内容一同加到iframe下的form中,传递到后台PHP脚本,以$_REQUEST等变量获得这些值。 后台输出保留的file_info.txt内容如下:
array ( 'file' => array ( 'name' => 'firefox-java.txt', 'type' => 'text/plain', 'tmp_name' => 'D:\\Tools\\xampp\\tmp\\phpED45.tmp', 'error' => 0, 'size' => 250, ), 0 => array ( 'test' => 'test', 'ok' => 'ok', 'PHPSESSID' => 'e379fd4fb2abca6e802a1302805a5535', ), )
ajaxfileupload.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
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 = $( '<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>' );
var oldElement = $( '#' + fileElementId);
var newElement = $(oldElement).clone();
$(oldElement).attr( 'id' , fileId);
$(oldElement).before(newElement);
$(oldElement).appendTo(form);
//set attributes
$(form).css( 'position' , 'absolute' );
$(form).css( 'top' , '-1200px' );
$(form).css( 'left' , '-1200px' );
$(form).appendTo( 'body' );
return form;
},
addOtherRequestsToForm: function (form,data)
{
// add extra parameter
var originalElement = $( '<input type="hidden" name="" value="">' );
for ( var key in data) {
name = key;
value = data[key];
var cloneElement = originalElement.clone();
cloneElement.attr({ 'name' :name, 'value' :value});
$(cloneElement).appendTo(form);
}
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 = new Date().getTime()
var form = jQuery.createUploadForm(id, s.fileElementId);
if ( s.data ) form = jQuery.addOtherRequestsToForm(form,s.data);
var io = jQuery.createUploadIframe(id, s.secureuri);
var frameId = 'jUploadFrame' + id;
var formId = 'jUploadForm' + id;
// Watch for a new set of requests
if ( s.global && ! jQuery.active++ )
{
jQuery.event.trigger( "ajaxStart" );
}
var requestDone = false ;
// Create the request object
var xml = {}
if ( s.global )
jQuery.event.trigger( "ajaxSend" , [xml, s]);
// Wait for a response to come back
var uploadCallback = function (isTimeout)
{
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 a local callback was specified, fire it and pass it the data if ( s.success )
s.success( data, status ); // Fire the global callback if ( s.global )
jQuery.event.trigger( "ajaxSuccess" , [xml, s] );
} else
jQuery.handleError(s, xml, status); } catch (e)
{
status = "error" ;
jQuery.handleError(s, xml, status, e); } // The request was completed if ( s.global )
jQuery.event.trigger( "ajaxComplete" , [xml, s] );
// Handle the global AJAX counter if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
// Process result if ( s.complete )
s.complete(xml, status); jQuery(io).unbind() setTimeout( function ()
{ try
{
$(io).remove();
$(form).remove();
} catch (e)
{
jQuery.handleError(s, xml, null , e);
}
}, 100)
xml = null
} }
// Timeout checker
if ( s.timeout > 0 )
{
setTimeout( function (){
// Check to see if the request is still happening if ( !requestDone ) uploadCallback( "timeout" );
}, s.timeout); }
try
{
// var io = $('#' + frameId);
var form = $( '#' + formId);
$(form).attr( 'action' , s.url);
$(form).attr( 'method' , 'POST' );
$(form).attr( 'target' , frameId);
if (form.encoding)
{
form.encoding = 'multipart/form-data' ;
} else {
form.enctype = 'multipart/form-data' ;
} $(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 );
}
return {abort: function () {}};
},
uploadHttpData: function ( r, type ) {
var data = !type;
data = type == "xml" || data ? r.responseXML : r.responseText;
// If the type is "script", eval it in global context
if ( type == "script" )
jQuery.globalEval( data ); // Get the JavaScript object, if JSON is used.
if ( type == "json" )
eval( "data = " + data );
// evaluate scripts within html
if ( type == "html" )
jQuery( "<div>" ).html(data).evalScripts();
//alert($('param', data).each(function(){alert($(this).attr('value'));}));
return data;
}
}) |