最近在看《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对象自己创建的,切不可自己修改.
这样我们检测一下目标路径.
发现图片确实已经上传成功。因此,简单的异步上传文件的例子就说到这里,我们可以发现,还可以进一步完善这个例子,例如添加一个进度监听事件,查看文件目前上传的大小情况以及终止或暂停文件上传等等。要丰富这个功能的话,确实需要点能力。不过,我会慢慢的学习并且做一些案例来讲解。
最后给大家推荐一个百度团队开发的文件上传组件。点击打开链接