发文件上HTTP上传进度条效果是如何实现的
转自w18.net
既然是技术论坛,我来抛砖引玉一下,fawenjian的上传进度条可以说是一个得意之作,我们现在已经在几个项目中使用,效果还行,想在整理一下公布出来,希望对大家有用。一定知无不言,言无不尽!
[原理]
大家知道,要在WEB上做上传进度不是一件容易的事情,比如我要把某文件POST到upload.php,实际上整个流程是这样子的:IE/Firefox把文件打包好,发送一个请求给服务器,Apache(注意不是php脚本)首先接受这个请求,整个上传的过程(数据的通讯)完成以后才把上传的文件存好,一般是放在/tmp/***文件夹里面,然后再执行php的move_uploaded_file
就是说想要在上传过程当中知道文件已经上传的大小,纯粹用php是没有办法的。咦?有人会问,现在网上可以下载一个php的upload进度显示程序是如何做到的呢? 我研究了一下那个程序,他用php监听了另外一个端口处理http请求,就是说他的文件不是post给apache的,是直接post到服务器的另外一个端口,而这个端口由一个php程序控制。就是说他用php写了一个Apache。当然这个Apache可以用php写,也可以用java或者c++写。
就是说再整个上传的过程中,Apache知道发生什么事情,除了自己写一个Apache以外,还有一个办法,就是做一个Apache的插件,Apache的插件有几种制作的方法,一种是用c来写,然后重新编译apache或者编译一个apache的动态链接库,还有一种简便的方法是用mod_perl,apache的一个mod,安装好以后,apache支持用perl写的mod,这里用到的是Apache2 + mod_perl2, 后面再说为什么apache1做不到
[实施步骤]
第一步:安装好apache2和mod_perl2
确保mod_perl2配置成功(这个过程,说复杂也复杂,说简单也简单,大家都知道这个要靠自己摸索了,用ubuntu或者gentoo估计实施起来容易一些,直接apt-get或者emerge就可以了)
第二步:安装我们的插件
我上传到附件了,一个rar, 解压后有一个文件夹Upload, Upload下有一个文件Filter.pm,是perl的一个库文件。要把整个文件夹放在Perl能够找到的地方,比如我是把Upload文件夹放在/usr/share/perl5/下的,这下面还有perl其他的一些库文件
第三步:配置apache
比如,我要监听上传到/album_upload_file.php文件的进度,我要在apache的配置文件中加入这样一段
CODE:
<Location /album_upload_file.php>
SetHandler modperl
PerlInputFilterHandler Upload::Filter::handler
</Location>
意思就是说,上传到/album_upload_file.php的每一个字节都会经过Upload::Filter这个程序(为什么要然perl一定能找到它的原因就在这里)
Upload::Filter这个程序的功能是,不停的把上传的情况写到一个文件,什么文件呢? 这个文件名在上传的时候指定,比如post到的地址是/album_upload_file.php?xxxxxxxxx1234, 那这个文件就是/tmp/up_xxxxxxxxx1234, 就是说xxxxxxxxx1234必须唯一。这个文件的内容很简单,比如“500|1000” 就是说上传了500个字节,整个请求1000个字节。
到这里,可能有些同学已经知道我们怎么实现的了,我们一个iframe正在post,另外一个iframe不停的读取/tmp/up_xxxxxxxxx1234文件的内容出来,进度就有办法可以显示了。
第四步:完成上传页面
1. 要有一个用于上传的隐藏iframe
CODE:
2. 要有一个form把文件post到这个iframe(这个地方要自己修改一下)
CODE:
这里注意两个问题,第一个是target, 第二个是$videohash是我们生成的唯一的字符串
3. 要有一些div显示上传进度和js处理上传过程
DIV
CODE:
<p>
<div id="bar" style="background-color:#dee7ec;border:solid 1px #8cacbb; width:0; height:18px"></div>
<span id="percent"></span>
</p>
<p>正在上传视频:<span id="uploaded"></span> kb </p>
<p>文件总大小:<span id="total"></span> kb </p>
</div>
JS
CODE:
<script language="javascript" src="http://ffserver.coollittlethings.com/JS/ajax.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
function start(){
MM_findObj(‘upload_status’).style.display=’block’;
uploading();
}
function uploading(){
var r = Math.round(Math.random()*10000);
var url = "checkupload.php?r="+r+"&uid=<?= $videohash ?>";
var ajax = new Ajax();
var ajax_o = new Ajax_upload_object();
ajax.get(url,ajax_o);
}
var timer;
function Ajax_upload_object() {
this.callback = handle;
function handle(response){
var s = response.split("|");
var uploaded_size = s[0];
if(isNaN(uploaded_size)) return;
var total_size = s[1];
if(isNaN(total_size)) return;
var uploaded_percent;
if(total_size!=0) uploaded_percent = Math.round(uploaded_size/total_size*100)+’%';
else uploaded_percent = "0%";
//alert(uploaded_percent);
MM_findObj(‘bar’).style.width = uploaded_percent;
var uploaded_kb = Math.round(uploaded_size/1024);
var total_kb = Math.round(total_size/1024);
MM_setTextOfLayer(‘percent’,”,uploaded_percent);
MM_setTextOfLayer(‘uploaded’,”,uploaded_kb);
MM_setTextOfLayer(‘total’,”,total_kb);
timer = setTimeout("uploading()",1000);
}
}
</script>
代码由kingmax贡献,中间用到一些Ajax技术,可能要kingmax开一个专题讲一下,或者转一下之前的blog?
这里唯一要解释的一下就是,其中一行代码
var url = "checkupload.php?r="+r+"&uid=<?= $videohash ?>";
中checkupload.php这个php什么也不做,就直接显示/tmp/up_$videohash的内容
r是一个随机数,对php来说是没用的参数,是用来避免ajax的cache的机制的。
上传完以后怎么办呢?good question!
上传实际上由/album_upload_file.php处理,上传完成以后upload_iframe显示的页面实际上是/album_upload_file.php的内容,这个时候在/album_upload_file.php中执行一段你想要执行的js就ok了
[总结]
这个方法优点就是完全不干涉上传的处理,只是监听过程,也不用自己写web server,缺点就是要掌握服务器的控制权,虚拟主机不行。为发扬“知其然,知其所以然的精神”,下一帖更大家分析一下这个不足40行代码的神秘的Fiter.pm干了些什么,大家会发现mod_perl其实可以做很多事情,比如控制apache线程,处理盗链等问题