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 相关文章推荐
java EJB 加密与解密原理的一个例子
Jan 11 PHP
PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
Apr 09 PHP
单一index.php实现PHP任意层级文件夹遍历(Zjmainstay原创)
Jul 31 PHP
深入解析php之sphinx
May 15 PHP
使用php清除bom示例
Mar 03 PHP
ThinkPHP模板Switch标签用法示例
Jun 30 PHP
PHP反射使用实例和PHP反射API的中文说明
Jul 02 PHP
PHP连接MySQL数据的操作要点
Mar 20 PHP
php实现粘贴截图并完成上传功能
May 17 PHP
详解PHP中的null合并运算符
Dec 30 PHP
php实现购物车功能(上)
Jul 23 PHP
PHP判断表达式中括号是否匹配的简单实例
Oct 22 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 REMOTE_ADDR之获取访客IP的代码
2008/04/22 PHP
php中使用parse_url()对网址进行解析的实现代码(parse_url详解)
2012/01/03 PHP
通过php快速统计某个数据库中每张表的数据量
2012/09/04 PHP
php简单获取目录列表的方法
2015/03/24 PHP
实例分析PHP中PHPMailer发邮件
2017/12/13 PHP
CSS和JS标签style属性对照表(方便js开发的朋友)
2010/11/11 Javascript
读Javascript高性能编程重点笔记
2016/12/21 Javascript
jQuery模拟窗口抖动效果
2017/03/15 Javascript
JavaScript运动框架 多值运动(四)
2017/05/18 Javascript
vue.js通过路由实现经典的三栏布局实例代码
2018/07/08 Javascript
angular.js实现列表orderby排序的方法
2018/10/02 Javascript
小程序云开发之用户注册登录
2019/05/18 Javascript
vue父子组件通信的高级用法示例
2019/08/29 Javascript
微信小程序wx.navigateTo方法里的events参数使用详情及场景
2020/01/07 Javascript
vue axios封装httpjs,接口公用配置拦截操作
2020/08/11 Javascript
[52:40]完美世界DOTA2联赛PWL S2 Magma vs GXR 第一场 11.29
2020/12/02 DOTA
Python基于time模块求程序运行时间的方法
2017/09/18 Python
利用python的socket发送http(s)请求方法示例
2018/05/07 Python
python tkinter界面居中显示的方法
2018/10/11 Python
Python并行分布式框架Celery详解
2018/10/15 Python
python多进程(加入进程池)操作常见案例
2019/10/21 Python
在 Pycharm 安装使用black的方法详解
2020/04/02 Python
python中rb含义理解
2020/06/18 Python
怎么快速自学python
2020/06/22 Python
基于Python爬取51cto博客页面信息过程解析
2020/08/25 Python
DRF使用simple JWT身份验证的实现
2021/01/14 Python
利用 CSS3 实现的无缝轮播功能代码
2017/09/25 HTML / CSS
浅谈HTML5 Web Worker的使用
2018/01/05 HTML / CSS
DeinDesign德国:设计自己的手机壳
2019/12/14 全球购物
PHP面试题及答案二
2015/05/23 面试题
药剂专业个人求职信范文
2014/04/29 职场文书
银行自荐信怎么写
2015/03/05 职场文书
《黄山奇石》教学反思
2016/02/18 职场文书
工人先锋号事迹材料(2016精选版)
2016/03/01 职场文书
总结Python变量的相关知识
2021/06/28 Python
对讲机知识
2022/04/07 无线电