本程序是利用3.x的Firefox浏览器可以读取本地文件的特性,实现通过xmlHttPRequest上传大文件功能,并在可以上传过程中动态显示上传进度。略加修改,并与服务器端配合,可以实现断点续传等诸多功能。
本例主要是研究FireFox的file-input节点的一些特性,其他客户端应用,如Flash、Sliverlight等,在实现客户端大文件上传时,在数据传输与服务器端存储等方面,与本例的思路基本一致。
注意:文件体积似乎有临界点,但这个临界点是多少尚未确认。建议不要用此方法上传超过100M的文件。
以下是客户端javascript代码
复制代码 代码如下:
/*
* FireFoxFileSender version 0.0.0.1
* by MK winnie_mk(a)126.com
*
* 【本程序仅限于FireFox3.x版本,其他浏览器是否可以运行未做测试。】
* 【测试通过:FireFox 3.6.8 / Apache/2.2.11 (Win32) php/5.2.6 】
* ******************************************************************************
* 本程序是利用3.x的FireFox浏览器可以读取本地文件的特性
* 实现通过xmlhttpRequest上传大文件功能
* 并在可以上传过程中动态显示上传进度
* 略加修改,并与服务器端配合,可以实现断点续传等诸多功能
* 本例主要是研究FireFox的file-input节点的一些特性
* 其他客户端应用,如Flash、Sliverlight等,在实现客户端大文件上传时
* 在数据传输与服务器端存储等方面,与本例的思路基本一致
* 注意:文件体积似乎有个临界点,但这个临界点是多少尚未确认。建议不要用此方法上传超过100M的文件。
* ******************************************************************************
*/
function FireFoxFileSender(config){
var conf = config || {};
/*
* 错误信息队列
*/
this.errMsg = [];
/*
* 判断各参数是否齐备
*/
this.f = typeof conf.file == 'string' ?
document.getElementById(conf.file) : conf.file;
if(!this.f){ this.errMsg.push('Error: Not set the input file.'); }
else if(this.f.files.length < 1){ this.errMsg.push('Error: Not select a file.'); }
else {
this.fileName = this.f.value;
/*
* 在尝试直接发送二进制流时失败,改用发送base64编码数据。
*/
this.data = (this.data = this.f.files[0].getAsDataURL())
.substr(this.data.indexOf(',') + 1);
this.length = this.data.length;
/*
* 文件实际大小
*/
this.fileSize = this.f.files[0].fileSize;
/*
* 文件类型
*/
this.contentType = this.f.files[0].fileType;
}
/*
* 服务器端接收地址
*/
this.url = conf.url;
if(!this.url){
this.errMsg.push('Error: Not set the instance url to send binary.');
}
/*
* 发送数据包的大小。默认100kb
*/
this.packageSize = conf.packageSize || 102400;
/*
* 每次发送数据包大小应为4的倍数,确保服务器端转换base64编码正确。
*/
if(this.packageSize % 4 != 0)
this.packageSize = parseInt(this.packageSize / 4) * 4;
this.onSendFinished = conf.onSendFinished || null;
this.onSending = conf.onSending || null;
this.onError = conf.onError || null;
}
FireFoxFileSender.prototype = {
/*
* 记录当前发送的数据
*/
currentData : null,
/*
* 记录读取位置
*/
position : 0,
/*
* 数据大小。该值为base64字符串的长度。
*/
length : -1,
/*
* 检查错误队列,尝试触发onError事件
*/
checkError : function(){
if(this.errMsg.length > 0){
/*
* 触发onError事件
*/
typeof this.onError == 'function' && this.onError(this.errMsg);
return;
}
},
/*
* 创建XMLHttpRequest
*/
createSender : function(){
var xhr = new XMLHttpRequest();
xhr.open('POST', this.url, true);
var _ = this;
xhr.onreadystatechange = function(){
/*
* 当服务器段响应正常,则循环读取发送。
*/
if(xhr.readyState == 4 && xhr.status == 200){
/*
* 触发onSending事件
*/
if(typeof _.onSending == 'function') _.onSending(_, xhr);
/*
* 延时发送下一次请求,否则服务器负担过重
*/
var send = setTimeout(function(){
_.send();
clearTimeout(send);
send = null;
}, 100);
}
}
return xhr;
},
/*
* 发送数据
*/
send : function(){
this.checkError();
/*
* 获取当前要发送的数据
*/
this.currentData = this.data.substr(this.position, this.packageSize);
/*
* 更改postion,模拟数据流移位
*/
this.position += this.currentData.length;
/*
* 如果读取字符串长度大于0,则发送该数据
* 否则触发onSendFinished事件
*/
if(this.currentData.length > 0) {
var xhr = this.createSender();
/*
* 自定义头部信息,通知服务器端文件相关信息
* 实际应用时可修改此部分。
*/
xhr.setRequestHeader('#FILE_NAME#', this.fileName);
xhr.setRequestHeader('#FILE_SIZE#', this.length);
xhr.setRequestHeader('#CONTENT_TYPE#', this.contentType);
xhr.send(this.currentData);
} else if(typeof this.onSendFinished == 'function') {
/*
* 触发onSendFinished事件
*/
this.onSendFinished(this);
}
},
/*
* 计算已发送数据百分比
*/
percent : function(){
if(this.length <= 0 ) return -1;
return Math.round((this.position / this.length) * 10000) / 100;
},
onSendFinished : null, //该事件是以本地数据发送完成为触发,并不是服务器端返回的完成信息。
onSending : null,
onError : null
}