最近在看《javascript權威指南》的時候,發現它在Ajax技術上講解了關於javascript上傳文件的內容。這也是博主在多年前的一個困擾,曾經解決的辦法就是借用別人的js控件,因此對這個技術理解甚少。當然,解決異步文件上傳的方式有許多種,比如利用flash插件,或者是嵌入iframe元素,等等。接下來就開始介紹我們如何通過Ajax技術進行文件上傳。
首先,支持文件上傳的瀏覽器是有所限制的。其關鍵在於瀏覽器是否支持XMLHttpRequest level 2 的 API,在這裏會有一些對象需要介紹一下。
- File對象, 這個對象不能通過構造函數生成,只能從 input file 控件的 files[]列表獲取.
- FormData對象,這個對象就是form表單的載體。
接下來我們先製作一個簡單的html頁面,其中有兩個關鍵元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上傳</title>
<script type="text/javascript" src="/js/ajax.js"></script>
</head>
<body>
<input type="file" name="file" id="file" />
<button id="go">提交</button>
</body>
</html>
然後我們發現,該頁面存在一個ajax.js腳本. 這個腳本主要就是爲我們提供一些ajax的方法
function createXHR(){
if( typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest();
}
if(typeof ActiveXobject == "undefined"){
throw new Error(" not support ");
}
//判斷是否爲 IE
if(typeof arguments.callee.activeString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp3.0","MSXML2.XMLHttp"],
i,len;
for (var i = 0;i<versions.length;i++) {
try{
new ActiveXobject(versions[i]);
arguments.callee.activeString=versions[i];
break;
}catch(ex){
// no action
}
};
}
return new ActiveXobject(arguments.callee.activeString);
}
/**
*文件表單上傳
*
*@param {String} url
*@param {FormData} form
*@param {Function} callback
*/
function formData(url,form,callback){
var xhr = createXHR();
xhr.open("POST",url);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && (xhr.status == 200)){
callback(xhr);
}
}
xhr.send(form);
}
只憑這些是不能完成文件上傳的.我們在html頁面中添加一些腳本代碼,最好是在body元素的後部添加script腳本.
function $(id){return document.getElementById(id);} //簡化通過id獲取元素節點
var filedata =[];//生成一個空數組
// 爲file控件添加一個change事件
$("file").addEventListener("change",function(e){
var file = this.files[0]; //默認獲取第一個文件 返回的是一個File對象
//讀取文件的基本屬性
var data = { name : file.name, //文件的名稱
size : file.size, //文件的大小
type : file.type || "", //文件的類型 如果沒有文件類型則爲空
id : (file.lastModifiedDate + "").replace(/\W/g, '') + this.size + this.type.replace(/\W/g, '') //生成文件唯一id
};
// 將文件名的id存入數組filedata中
filedata.push(data.id);
// 擴展filedata數組,通過文件唯一id 保存文件的基本信息 以及 File文件對象
filedata[data.id] = {info:data,file:file};
console.log(filedata);
},false);
//爲按鈕添加一個click事件
$("go").addEventListener("click",function(){
//判斷等待上傳的文件列表是否爲 0
if(filedata.length <= 0 ){
alert('請選擇至少一個文件');
return;
}
var id = filedata[0];
var data = filedata[id];
// form表單對象
var form = new FormData();
// 請求url地址
var url ="http://localhost/demo/uploadFile";
// 添加若干參數 第一個參數爲參數名,第二個參數爲參數值
form.append("name",data.info.name);
form.append("type",data.info.type);
form.append("fileid",id);
// 添加文件對象,將文件以字節流的方式作爲請求主體.
form.append("file",data.file);
//調用寫好的函數
formData(url,form,function(res){
//請求-響應成功
//刪除上傳成功的文件對象
delete filedata[id];
filedata.shift();
console.log(res.responseText);
});
},false);
//判斷瀏覽器是否支持腳本上傳功能
if(FormData == undefined){
throw new Error("瀏覽器不支持");
}
這裏並沒有做一些比較好看的css效果,這樣是爲了簡化過程.我們只要看到結果就可以了.寫完了前端的代碼.我們該考慮後端如何寫.
首先我們得確認後端的配置是否允許文件上傳的功能,我的服務端代碼是用PHP寫的,所有要修改一下php.ini 的一些配置,至於如何修改可以查看網絡的資源,搜索關鍵字: php 文件上傳 配置修改.
在確保這些都沒有問題的情況下,我來給大家展示php的端代碼.
<?php
$filename = $_POST['name'];
$fileId = $_POST['fileid'];
$postfix = explode(".",$filename); //獲取後綴
$postfix = empty($postfix[1])?"":".".$postfix[1];
// public_path() 返回的是一個絕對的路徑 可自己定義
$filepath = public_path().'/uploads/'.$fileId.$postfix;
//文件寫入
file_put_contents($filepath, file_get_contents($_FILES["file"]["tmp_name"]), FILE_APPEND);
echo json_encode(array("rs"=>"success","path"=>"/uploads/".$fileId.$postfix));
exit;
?>
下面是測試截圖- Firefox 瀏覽器
我們選擇一個文件後,會觸發 file元素的onchange事件
上面是 filedata 的一些屬性值。
然後我們點擊按鈕上傳後,查看一下我們的請求響應頭部
這個請求頭部類型爲我們的FormData對象自己創建的,切不可自己修改.
這樣我們檢測一下目標路徑.
發現圖片確實已經上傳成功。因此,簡單的異步上傳文件的例子就說到這裏,我們可以發現,還可以進一步完善這個例子,例如添加一個進度監聽事件,查看文件目前上傳的大小情況以及終止或暫停文件上傳等等。要豐富這個功能的話,確實需要點能力。不過,我會慢慢的學習並且做一些案例來講解。
最後給大家推薦一個百度團隊開發的文件上傳組件。點擊打開鏈接