一直困扰着Web开发人员多年的一个问题是如何实时信息添加到自己的应用程序,如文件上传进度条。 用户没有耐心,他们不想坐等,而浏览器是做什么,不知是否已冻结或者,如果他们有一个缓慢的连接。 提供了一个进度指示器为用户提供有用的信息,并让他们知道到底发生了什么事情。
在第一个想到的,你可能会认为完成这可以很容易地通过首先获得从用户的计算机中的文件的大小,然后进行一些简单的计算对目录下的文件被上传到服务器上完成。 关于第二个想法,你会发现事情并没有想象中的那么简单。
JavaScript可以访问一个文件的名称,类型,甚至宽度和本地图像的高度,但它不是直到HTML5,它可以访问一个文件的大小 。 不幸的是,HTML5仍然是一个没有完成的标准还没有,而不是统一在所有浏览器都支持。另一种解决方案是依靠一个Flash,Java或安装ActiveX插件,不,谢谢,我会通过。 另一种解决方案是安装可选PHP缓存扩展,但根据您的托管环境中可能无法使用,这似乎有点小题大做,这样一个小任务,比如这个。
这似乎就像所有的选项都充满了滋扰,任务已经迅速成为一个头痛的问题。 但在尤达的话来说,“没有......还有一个。”
其中的原因有很多我喜欢PHP的是,它使看似困难的任务很容易。 在PHP 5.4中,他们已经再次用一组新的配置选项,做到了session.upload_progress 。
在本文中,我将向您展示如何这个功能可以被用来创建一个简单的上传进度条没有任何外部库或浏览器的依赖。 我将首先讨论它是如何工作的,然后我会引导您完成创建完成的任务(一个上传表单,一些JavaScript,一点点CSS和文件返回载的状态)所需的四个文件。
会议上传进度
除了通常的要求,以允许文件上传,还有两个来跟踪进度。 该session.upload_progress.enabled指令必须被启用,并且必须有一个隐藏字段中的Web表单与指定的名称session.upload_progress.name指令。当session.upload_progress.enabled是真实的(因为它是在默认情况下在PHP 5.4及想必以后)和$_POST[session.upload_progress.name ]中关于文件的传输上载,信息发送是可用的$_SESSION超全局数组。
该print_r()输出的$_SESSION数组看起来类似于下面的一个文件传输过程中:
排列 ( [upload_progress_myForm] =>阵列 ( [START_TIME] => 1323733740 [CONTENT_LENGTH] => 721127769 [bytes_processed] => 263178326 [完成] => [文件] =>阵列 ( [0] =>阵列 ( [FIELD_NAME] => USERFILE [名称] =>的ubuntu-10.04.3-桌面i386.iso [tmp_name的值] => [错误] => 0 [完成] => [START_TIME] => 1323733740 [bytes_processed] => 263178026 ) ) ) )
当你在开发本地或快速网络上,并上传小文件,您将无法以目视观察进度,因为转移发生的这么快。 在这种情况下,你可能想尝试传输大文件。 请确保在您设置php.ini文件,允许较大的上载,特别是post_max_size和upload_max_filesize指令,然后确认他们是理智的值时,你去生产。
创建窗体
我将介绍的第一个文件是上传表单。 只是为了让事情尽可能简单,这个例子将张贴到本身只能处理一个文件上传的时间。 此外,我不会打扰保存该文件已被上传后。
下面是代码form.php :
<?PHP 如果($ _SERVER [ “REQUEST_METHOD” ] == “POST” &&!空($ _FILES [ “userfile的” ])){ / / move_uploaded_file()以 } ?> <HTML> <HEAD> <TITLE>文件上传进度条</ TITLE> <链接相对= “样式表” 类型= “文/ CSS” HREF = “style.css文件” > </ HEAD> <BODY> <分区编号= “bar_blank” > <分区编号= “bar_color” > </ DIV> </ DIV> <分区编号= “状态” > </ DIV> <形式的行动= “<PHP的回声$ _SERVER?[” PHP_SELF “];?>” 方法= “POST” ID = “myForm的” 是enctype = “多部分/窗体的数据” 目标= “hidden_iframe” > <输入类型= “隐藏” 值= “myForm的” 名称= “<?PHP的回声ini_get(” session.upload_progress.name ?“);>” > <输入类型= “文件” 名称= “USERFILE” > <BR> <输入类型= “提交” 值= “开始上传” > </ FORM> <iframe的ID = “hidden_iframe” 名称= “hidden_iframe” SRC = “关于:空白” > </ IFRAME> <脚本类型= “文/ JavaScript的” SRC = “的script.js” > </ SCRIPT> </ BODY> </ HTML>
在本示例中的代码来实际处理该文件已被省略,让事情变得简单。 如果你有兴趣在这样的代码应该是什么样子,请查看文章文件上传用PHP由Timothy Boronczyk。
头部分,它提供了网页的标题和包括样式表之后,你会发现一个小集合的div元素。 ID为“bar_blank”的DIV是容器的进度条。 ID为“bar_color”在div将动态更新的文件上传进度。 “状态”的div将显示百分之上传的数值。
该窗体设置为提交给同一个URL,并将其目标属性指向一个隐藏的iframe元素。 提交表单到一个隐藏的框架可以让你保持访问者在同一页上,而工作在后台正在做。 其实,这是一种常见的做法做“的Ajax文件上传”的时候,因为它是不可能直接发送使用JavaScript的一个文件的内容XmlHttpRequest对象。
在表格中,特殊的隐藏字段需要填充$_SESSION数组,接着出现一个文件上传输入和提交按钮。 提交表单将触发一个名为JavaScript函数startUpload()将被包含的JavaScript文件中定义。
在页面的底部是隐藏的框架,其形式将发布和进口script.js文件。
添加一些样式
下一个文件, style.css ,是相当直接的。 我定义的进度条容器的大小并给予它一个1px的黑色边框,进度条的颜色,因为它的加载,无论是IFRAME和进度条是隐藏的。
#bar_blank { 边界:固体1px的#000 ; 高度:20像素; 宽度:300像素; } #bar_color { 背景色:#006666 ; 高度:20像素; 宽度:0PX ; } #bar_blank,#hidden_iframe { 显示:无; }
客户端功能
该script.js文件是最大的组文件。 它包含六大功能,我将在下面讨论。 很多人喜欢用jQuery来提供一些功能在这里,你当然可以这样做,如果你愿意的话,但我个人更喜欢老派的做法。 类似于如何日本人放在手工制作的货物价值较高的,我只是觉得更热衷于代码,如果是我自己。
功能toggleBarVisibility(){ 变种E =的document.getElementById(“bar_blank” ); e.style.display =(e.style.display == “块” ?) “ 无” :“块” ; } 功能createRequestObject(){ 变种的http; 如果(navigator.appName == “Microsoft Internet Explorer的” ){ HTTP = 新的ActiveXObject(“Microsoft.XMLHTTP” ); } 否则{ HTTP = 新的XMLHttpRequest(); } 返回的http; } 功能sendRequest将(){ 变种的http = createRequestObject(); http.open(“GET” ,“progress.php” ); http.onreadystatechange = 函数(){用handleResponse(HTTP);}; http.send(空); } 功能用handleResponse(HTTP){ 无功响应; 如果(http.readyState == 4){ 响应= http.responseText; 的document.getElementById(“bar_color” 。)共0则回应+ “%” ; 的document.getElementById( “ 地位” 。)的innerHTML =响应+ “%” ; 如果(响应<100){ 的setTimeout(“sendRequest将()” ,1000); } 否则{ toggleBarVisibility(); 的document.getElementById( “ 地位” 。)的innerHTML = “Done(完成)。” ; } } } 功能startUpload(){ toggleBarVisibility(); 的setTimeout(“sendRequest将()” ,1000); } (函数(){ 的document.getElementById(“myForm会” )的onsubmit = startUpload; })();
该toggleBarVisibility()函数上的“bar_blank”的div根据需要显示或隐藏进度条设置合适的样式。 最初,它开始时隐藏,但会一次上传开始出现,然后再次隐藏当上载完成。
该createRequestObject()函数创建一个XMLHttpRequest或ActiveXObject根据用户的浏览器对象。 这可能是该功能大多数人都期待的jQuery或其他一些JavaScript框架来提供。
该sendRequest()函数请求progress.php文件与一个GET请求,然后调用handleResponse()函数来处理返回的数据。
该handleResponse()函数处理从响应progress.php这将是依赖于文件上传进度多项1-100之间。 我也更新了“状态”的div适当的值。 如果电流的百分比低于100然后调用JavaScript的原生setTimeout()函数来1秒后发送的更新另一个请求(你可能需要调整该值如适用),否则我再次隐藏进度条和状态设置为“完成”。
该startUpload()函数使得载栏可见,并发送一个更新请求的1秒的延迟。 这个小的延迟是必要的,为了给上传时间才能启动。
最后一个功能是一个自执行的匿名函数,它注册startUpload()与表单的提交事件。
实时进展
带来一切融合在一起的最后一个文件是progress.php文件:
<?PHP 在session_start(); 美元的关键= ini_get (“session.upload_progress.prefix” )。“myForm的” ; 如果(!空($ _SESSION [ $关键])){ $电流= $ _SESSION [ $关键] [ “bytes_processed” ]; 共$ = $ _SESSION [ $关键] [ “CONTENT_LENGTH” ]; 回声$电流< $总?CEIL ($电流/ $总量* 100):100; } 否则{ 回声100; }
该脚本执行上传输的字节数目前在总文件大小,再乘以100,并四舍五入到给一个百分比分成一些简单的数学。
有关传输的信息,关键在于有一套与session.upload_progress.prefix指令的值的串联和隐藏session.upload_progress.name字段的值。 因为我的形式通过了“myForm会”,会议的重点是与确定ini_get("session.upload_progress.prefix") . "myForm"ini_get("session.upload_progress.prefix") . "myForm" 。
下面是在行动进度条的截图:
微调行为
PHP提供了一些额外的指令来帮助微调会话上传的行为,你应该知道的。 例如,session.upload_progress.cleanup ,这是默认设置为1,清理后,立即上传已经完成了额外的会话数据。 你必须要小心,以避免潜在的竞争条件。
再看看在代码progress.php ,你会发现,我检查,看看是否$_SESSION[$key]为空或不是,然后再继续。我的JavaScript函数火了每一秒,只要结果从返回progress.php小于100。 如果session.upload_progress.cleanup已启用,我的脚本获取99%的上传和1/2-second后上传完成后,$_SESSION[$key]不会为下一个检查存在的。 如果我不考虑到这一点,然后我的JavaScript函数可能保持射击,上传完成之后也是如此。
另外两个指令是session.upload_progress.freq和session.upload_progress.min_freq这两个决定多久了会议应更新。 频率的值可以在任一字节(即100)或总字节数的百分比(即2%)给出。 价值min_freq以秒为单位,并表示更新之间的最小秒数。 显然,如果min_freq设置为更新每隔1秒,这将是毫无意义的JavaScript来检查每100毫秒。
总结
你现在应该有一个牢固掌握如何创建使用会话上传进度功能的文件上传进度条。 展望未来,我鼓励你尝试上传多个文件,给予取消使用上传进行中的选项$_SESSION[$key]["cancel_upload"] ,或任何其他的想法你的思想可以鼓起。