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 mysql数据库操作类
Jun 04 PHP
解析如何屏蔽php中的phpinfo()函数
Jun 06 PHP
浅析PHP绘图技术
Jul 03 PHP
PHP修改session_id示例代码
Jan 08 PHP
ThinkPHP实现批量删除数据的代码实例
Jul 02 PHP
PHP获取服务器端信息的方法
Nov 28 PHP
php实现字符串反转输出的方法
Mar 14 PHP
PHP也能干大事之PHP中的编码解码详解
Apr 20 PHP
php筛选不存在的图片资源
Apr 28 PHP
Yii2中OAuth扩展及QQ互联登录实现方法
May 16 PHP
PHP设计模式之装饰器模式实例详解
Feb 07 PHP
详解关于php的xdebug配置(编辑器vscode)
Jan 29 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
PHP 数组排序方法总结 推荐收藏
2010/06/30 PHP
php解决抢购秒杀抽奖等大流量并发入库导致的库存负数的问题
2014/06/19 PHP
php返回json数据函数实例
2014/10/09 PHP
thinkphp文件引用与分支结构用法实例
2014/11/26 PHP
纯js网页画板(Graphics)类简介及实现代码
2012/12/24 Javascript
javascript搜索框效果实现方法
2015/05/14 Javascript
理解Javascript的动态语言特性
2015/06/17 Javascript
jquery实现带缩略图的全屏图片画廊效果实例
2015/06/25 Javascript
JavaScript实现跑马灯抽奖活动实例代码解析与优化(二)
2016/02/16 Javascript
JS 实现倒计时数字时钟效果【附实例代码】
2016/03/30 Javascript
学习使用bootstrap基本控件(table、form、button)
2016/04/12 Javascript
原生js实现addClass,removeClass,hasClass方法
2016/04/27 Javascript
JS基于构造函数实现的菜单滑动显隐效果【测试可用】
2016/06/21 Javascript
javascript实现的上下无缝滚动效果
2016/09/19 Javascript
Vuejs入门教程之Vue生命周期,数据,手动挂载,指令,过滤器
2017/04/19 Javascript
jQuery实现鼠标滑过预览图片大图效果的方法
2017/04/26 jQuery
微信小程序实现缓存根据不同的id来进行设置和读取缓存
2017/06/12 Javascript
JavaScript标准对象_动力节点Java学院整理
2017/06/27 Javascript
AngularJS 实现点击按钮获取验证码功能实例代码
2017/07/13 Javascript
JavaScript变量作用域及内存问题实例分析
2019/06/10 Javascript
JavaScript:ES2019 的新特性(译)
2019/08/08 Javascript
手把手带你入门微信小程序新框架Kbone的使用
2020/02/25 Javascript
VSCode搭建Vue项目的方法
2020/04/30 Javascript
详解vue高级特性
2020/06/09 Javascript
js实现带有动画的返回顶部
2020/08/09 Javascript
[03:22]DSPL第一期精彩集锦:酷炫到底!
2014/11/07 DOTA
python字典get()方法用法分析
2015/04/17 Python
对python中的six.moves模块的下载函数urlretrieve详解
2018/12/19 Python
python操作cfg配置文件方式
2019/12/22 Python
Django 自定义权限管理系统详解(通过中间件认证)
2020/03/11 Python
浅析Python 简单工厂模式和工厂方法模式的优缺点
2020/07/13 Python
HTML里显示pdf、word、xls、ppt的方法示例
2020/04/14 HTML / CSS
2015年党员创先争优公开承诺书
2015/04/27 职场文书
JavaScript 语句之常用 for 循环详解
2021/03/29 Javascript
Python数据结构之队列详解
2022/03/21 Python
Android学习之BottomSheetDialog组件的使用
2022/06/21 Java/Android