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 21 PHP
php 获取远程网页内容的函数
Sep 08 PHP
php获取数组中重复数据的两种方法
Jun 28 PHP
php实现统计邮件大小的方法
Aug 06 PHP
Java和PHP在Web开发方面对比分析
Mar 01 PHP
PHP5.3新特性小结
Feb 14 PHP
浅谈PHP eval()函数定义和用法
Jun 21 PHP
PHP mysqli事务操作常用方法分析
Jul 22 PHP
PHP实现重载的常用方法实例详解
Oct 18 PHP
利用php获得flv视频长度的实例代码
Oct 26 PHP
在云虚拟主机部署thinkphp5项目的步骤详解
Dec 21 PHP
php 多个变量指向同一个引用($b = &amp;$a)用法分析
Nov 13 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 daddslashes()和 saddslashes()有哪些区别分析
2012/10/26 PHP
初学prototype,发个JS接受URL参数的代码
2006/09/25 Javascript
div层的移动及性能优化
2010/11/16 Javascript
javascript的offset、client、scroll使用方法详解
2012/12/25 Javascript
javascript之typeof、instanceof操作符使用探讨
2013/05/19 Javascript
js中创建对象的几种方式示例介绍
2014/01/26 Javascript
通过url查找a元素并点击
2014/04/09 Javascript
NodeJS学习笔记之网络编程
2014/08/03 NodeJs
JavaScript数组常用操作技巧汇总
2014/11/17 Javascript
AspNet中使用JQuery上传插件Uploadify详解
2015/05/20 Javascript
jquery实现TAB选项卡鼠标经过带延迟效果的方法
2015/07/27 Javascript
js实现简洁的TAB滑动门效果代码
2015/09/06 Javascript
理解js回收机制通俗易懂版
2016/02/29 Javascript
Ionic实现页面下拉刷新(ion-refresher)功能代码
2016/06/03 Javascript
jquery中$.fn和图片滚动效果实现的必备知识总结
2017/04/21 jQuery
详解在 Angular 项目中添加 clean-blog 模板
2017/07/04 Javascript
vue之浏览器存储方法封装实例
2018/03/15 Javascript
详解React中合并单元格的正确写法
2019/01/08 Javascript
vue 图片裁剪上传组件的实现
2020/11/12 Javascript
Python 内置函数complex详解
2016/10/23 Python
Python使用django框架实现多人在线匿名聊天的小程序
2017/11/29 Python
查看django执行的sql语句及消耗时间的两种方法
2018/05/29 Python
Python实现矩阵相乘的三种方法小结
2018/07/26 Python
windows系统中Python多版本与jupyter notebook使用虚拟环境的过程
2019/05/15 Python
python3使用腾讯企业邮箱发送邮件的实例
2019/06/28 Python
python 使用shutil复制图片的例子
2019/12/13 Python
Python实现粒子群算法的示例
2021/02/14 Python
国家地理在线商店:Shop National Geographic
2018/06/30 全球购物
营销总经理岗位职责
2014/02/02 职场文书
食品采购员岗位职责
2014/04/14 职场文书
协议书模板
2014/04/23 职场文书
治超工作实施方案
2014/05/04 职场文书
群众路线个人对照检查材料2014
2014/09/26 职场文书
2014年学校禁毒工作总结
2014/12/23 职场文书
圣诞节开幕词
2015/01/29 职场文书
Python Pytorch查询图像的特征从集合或数据库中查找图像
2022/04/09 Python