FireFox浏览器使用Javascript上传大文件


Posted in PHP onOctober 30, 2013

本程序是利用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
}

/*
 * 上传按钮事件
 */
function send(fileID){
    var sender = new FireFoxFileSender(
        /*
         * 上传配置文件
         */
        {
            /*
             * input file 元素,可以是dom节点,也可以是id的字符串值
             */
            file : fileID,
            /*
             * 接收上传数据的服务器端地址
             */
            url : 'UPLOADER.php',
            /*
             * 每次发送数据包的大小。可根据服务器具体情况更改。IIS6默认为200K
             */
            packageSize : '200000',
            /*
             * 出现错误时触发该事件。本例仅在初始化时判断各参数是否齐备,并没有抛出发送过程中的错误。
             */
            onError : function(arrMsg){
                alert(arrMsg.join('\r\n'));
                sender = null;
                delete sender;
            },
            /*
             * 发送过程中触发该事件。本例中主要用于显示进度。
             */
            onSending : function(sd, xhr){
                var per = sd.percent();
                document.getElementById('Message').innerHTML = per + '% ';
                /*
                 * 该判断是在最后一次发送结束后,通过xhr的onreadystatechange事件触发的
                 * 如果传输过程中没有其他错误,基本可以确定为服务器端接收完成
                 */
                if(parseInt(per) == 100){ alert('服务器端接收完成'); }
            },
            /*
             * 该事件仅仅为【本地数据发送完成】时触发。
             * 请区别本地数据发送完成和服务器端返回完成信息这两种情况
             * 本例中并没有对服务器接收信息的情况做响应
             * 即使服务器端没有接收和保存任何数据
             * 只要确保xhr返回readyState == 4 和 status == 200
             * 发送就会继续进行
             * 服务器端如何返回完成信息可以通过更改接收数据页面的代码自定实现
             * 然后通过对xhr.responseText的值来做判断
             */
            onSendFinished : function(){
                alert('本地数据发送完成');
            }
        }
    );
    sender.send();
}

以下是服务器端php代码
/*
 * 获取输入信息
 */
$b64 = file_get_contents("php://input");
/*
 * 获取头部信息
 */
$headers = getallheaders();
$fileName = $headers['#FILE_NAME#'];
$contentType = $headers['#CONTENT_TYPE#'];
/*
 * 做一些判断和处理...
 */
/*
 * 以下是服务器端对发送数据的简单响应
 * - 假如有数据被post过来 则输出对base64转换为二进制流后,二进制流的长度
 * - 否则输出0
 * 这仅仅是一个例子,并且在js端没有接收这个信息
 * 同样,也可以采用在header中写入反馈信息等等方法
 * 回馈信息给客户端
 * 主要目的是确定上传过程中是否有其他问题出现
 * 以确保上传文件完整
 */
if(!empty($b64)){
    $stream = base64_decode($b64);
    echo strlen($stream);
    /*
     * 追加方式写入文件
     * 在此修改文件保存位置
     */
    $file = fopen('' . $fileName , 'a');
    if($file)
        if(fwrite($file, $stream))
            fclose($file);
} else echo '0';

客户端完整代码
<!DOCTYPE HTML>
  2  <html>
  3  <head>
  4  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5  <title>FireFoxFileSender - !! ONLY FOR FireFox !!</title>
  6  </head>
  7 
  8  <body>
  9  <script type="text/Javascript">
 10  /*
 11  * FireFoxFileSender version 0.0.0.1
 12  * by MK winnie_mk(a)126.com
 13  * 
 14  * 【本程序仅限于FireFox3.x版本,其他浏览器是否可以运行未做测试。】
 15  * 【测试通过:FireFox 3.6.8 / Apache/2.2.11 (Win32) PHP/5.2.6 】
 16  * *********************************************************************************
 17  * 本程序是利用3.x的FireFox浏览器可以读取本地文件的特性
 18  * 实现通过XMLHttpRequest上传大文件功能
 19  * 并在可以上传过程中动态显示上传进度
 20  * 略加修改,并与服务器端配合,可以实现断点续传等诸多功能
 21  * 本例主要是研究FireFox的file-input节点的一些特性
 22  * 其他客户端应用,如Flash、Sliverlight等,在实现客户端大文件上传时
 23  * 在数据传输与服务器端存储等方面,与本例的思路基本一致
 24  * 注意:文件体积似乎有个临界点,但这个临界点是多少尚未确认。建议不要用此方法上传超过100M的文件。
 25  * *********************************************************************************
 26  */
 27  function FireFoxFileSender(config){
 28     var conf = config || {};
 29     /*
 30      * 错误信息队列
 31      */
 32     this.errMsg = [];    
 33     /*
 34      * 判断各参数是否齐备
 35      */
 36     this.f = typeof conf.file == 'string' ? document.getElementById(conf.file) : conf.file;
 37     if(!this.f){ this.errMsg.push('Error: Not set the input file.'); }
 38     else if(this.f.files.length < 1){ this.errMsg.push('Error: Not select a file.'); }
 39     else {
 40         this.fileName = this.f.value;
 41         /*
 42          * 在尝试直接发送二进制流时失败,改用发送base64编码数据。
 43          */
 44         this.data = (this.data = this.f.files[0].getAsDataURL()).substr(this.data.indexOf(',') + 1);
 45         this.length = this.data.length;
 46         /*
 47          * 文件实际大小
 48          */
 49         this.fileSize = this.f.files[0].fileSize;
 50         /*
 51          * 文件类型
 52          */
 53         this.contentType = this.f.files[0].fileType;
 54     }
 55     /*
 56      * 服务器端接收地址
 57      */
 58     this.url = conf.url;
 59     if(!this.url){ this.errMsg.push('Error: Not set the instance url to send binary.'); }
 60     /*
 61      * 发送数据包的大小。默认100kb
 62      */
 63     this.packageSize = conf.packageSize || 102400;
 64     /*
 65      * 每次发送数据包大小应为4的倍数,确保服务器端转换base64编码正确。
 66      */
 67     if(this.packageSize % 4 != 0) this.packageSize = parseInt(this.packageSize / 4) * 4;
 68     
 69     this.onSendFinished = conf.onSendFinished || null;
 70     this.onSending = conf.onSending || null;
 71     this.onError = conf.onError || null;
 72 }
 73 FireFoxFileSender.prototype = {
 74     /*
 75      * 记录当前发送的数据
 76      */
 77     currentData : null,
 78     /*
 79      * 记录读取位置
 80      */
 81     position : 0,
 82     /*
 83      * 数据大小。该值为base64字符串的长度。
 84      */
 85     length : -1,
 86     /*
 87      * 检查错误队列,尝试触发onError事件
 88      */
 89     checkError : function(){
 90         if(this.errMsg.length > 0){
 91             /* 
 92              * 触发onError事件
 93              */
 94             typeof this.onError == 'function' && this.onError(this.errMsg);
 95             return;
 96         }
 97     },
 98     /*
 99      * 创建XMLHttpRequest
100      */
101     createSender : function(){
102         var xhr = new XMLHttpRequest();
103         xhr.open('POST', this.url, true);
104         var _ = this;
105         xhr.onreadystatechange = function(){
106             /*
107              * 当服务器段响应正常,则循环读取发送。
108              */
109             if(xhr.readyState == 4 && xhr.status == 200){
110                 /* 
111                  * 触发onSending事件
112                  */
113                 if(typeof _.onSending == 'function') _.onSending(_, xhr);
114                 /*
115                  * 延时发送下一次请求,否则服务器负担过重
116                  */
117                 var send = setTimeout(function(){
118                     _.send();
119                     clearTimeout(send);
120                     send = null;
121                 }, 100);                
122             }
123         }
124         return xhr;
125     },
126     /*
127      * 发送数据
128      */
129     send : function(){
130         this.checkError();
131         /*
132          * 获取当前要发送的数据
133          */
134         this.currentData = this.data.substr(this.position, this.packageSize);
135         /*
136          * 更改postion,模拟数据流移位
137          */
138         this.position += this.currentData.length;
139         /*
140          * 如果读取字符串长度大于0,则发送该数据
141          * 否则触发onSendFinished事件
142          */
143         if(this.currentData.length > 0) {
144             var xhr = this.createSender();
145             /*
146              * 自定义头部信息,通知服务器端文件相关信息
147              * 实际应用时可修改此部分。
148              */
149             xhr.setRequestHeader('#FILE_NAME#', this.fileName);
150             xhr.setRequestHeader('#FILE_SIZE#', this.length);
151             xhr.setRequestHeader('#CONTENT_TYPE#', this.contentType);
152             
153             xhr.send(this.currentData);
154         } else if(typeof this.onSendFinished == 'function') {
155             /*
156              * 触发onSendFinished事件
157              */
158             this.onSendFinished(this);
159         }
160     },
161     /*
162      * 计算已发送数据百分比
163      */
164     percent : function(){
165         if(this.length <= 0 ) return -1;
166         return Math.round((this.position / this.length) * 10000) / 100;
167     },
168     onSendFinished : null,    //该事件是以本地数据发送完成为触发,并不是服务器端返回的完成信息。
169      onSending : null,
170     onError : null
171 }
172 
173  /*
174  * 上传按钮事件
175  */
176  function%3
PHP 相关文章推荐
一个PHP日历程序
Dec 06 PHP
PHP 变量的定义方法
Jan 26 PHP
关于PHP中Object对象的笔记分享
Jun 28 PHP
PHP中获取变量的变量名的一段代码的bug分析
Jul 07 PHP
php中的登陆login实例代码
Jun 20 PHP
php编译安装php-amq扩展简明教程
Jun 25 PHP
php安装php_rar扩展实现rar文件读取和解压的方法
Nov 17 PHP
PHP遍历目录文件的常用方法小结
Feb 03 PHP
详谈php ip2long 出现负数的原因及解决方法
Apr 05 PHP
PHP实现打包zip并下载功能
Jun 12 PHP
PHP基于cookie实现统计在线人数功能示例
Jan 16 PHP
php时间戳转换代码详解
Aug 04 PHP
php使用ICQ网关发送手机短信
Oct 30 #PHP
PHP分页详细讲解(有实例)
Oct 30 #PHP
php预定义变量使用帮助(带实例)
Oct 30 #PHP
调整PHP的性能
Oct 30 #PHP
PHP数据过滤的方法
Oct 30 #PHP
php另类上传图片的方法(PHP用Socket上传图片)
Oct 30 #PHP
使用Curl进行抓取远程内容时url中文编码问题示例探讨
Oct 29 #PHP
You might like
IIS6+PHP5+MySQL5+Zend Optimizer+phpMyAdmin安装配置图文教程 2009年
2009/06/08 PHP
PHP常用操作类之通信数据封装类的实现
2017/07/16 PHP
理解Javascript_09_Function与Object
2010/10/16 Javascript
浅析ajax请求json数据并用js解析(示例分析)
2013/07/13 Javascript
jquery用get实现ajax在ie里面刷新不进入后台解决方法
2013/08/12 Javascript
js操作IE浏览器弹出浏览文件夹可以返回目录路径
2014/07/14 Javascript
js获取当前时间显示在页面上并每秒刷新
2014/12/24 Javascript
javascript 闭包详解
2015/07/02 Javascript
javascript中数组方法汇总
2015/07/07 Javascript
JavaScript开发者必备的10个Sublime Text插件
2016/02/27 Javascript
简单的jQuery banner图片轮播实例代码
2016/03/04 Javascript
解决jQuery ajax请求在IE6中莫名中断的问题
2016/06/20 Javascript
如何用JS判断两个数字的大小
2016/07/21 Javascript
基于jQuery实现发送短信验证码后的倒计时功能(无视页面关闭)
2016/09/02 Javascript
详解angularjs中如何实现控制器和指令之间交互
2017/05/31 Javascript
Angular4学习笔记之根模块与Ng模块
2017/09/09 Javascript
详解基于Koa2开发微信二维码扫码支付相关流程
2018/05/16 Javascript
vue自定义指令用法经典实例小结
2019/03/16 Javascript
JS定义函数的几种常用方法小结
2019/05/23 Javascript
JavaScript的Proxy可以做哪些有意思的事儿
2019/06/15 Javascript
vue 实现基础组件的自动化全局注册
2020/12/25 Vue.js
vue实现轮播图帧率播放
2021/01/26 Vue.js
深入探究Python中变量的拷贝和作用域问题
2015/05/05 Python
Pyhton中单行和多行注释的使用方法及规范
2016/10/11 Python
python3实现ftp服务功能(客户端)
2017/03/24 Python
python中requests库session对象的妙用详解
2017/10/30 Python
django2 快速安装指南分享
2018/01/05 Python
Python使用SQLite和Excel操作进行数据分析
2018/01/20 Python
python3 http提交json参数并获取返回值的方法
2018/12/19 Python
python 叠加等边三角形的绘制的实现
2019/08/14 Python
英国派对礼服和连衣裙购物网站:TFNC London
2018/07/07 全球购物
成教毕业生自我鉴定
2013/10/23 职场文书
房产转让协议书(2014版)
2014/09/30 职场文书
堂吉诃德读书笔记
2015/06/30 职场文书
2016年助残日旅游活动总结
2016/04/01 职场文书
JavaScript原型链中函数和对象的理解
2022/06/16 Javascript