首页 理论教育Nginx文件上传进度实现

Nginx文件上传进度实现

【摘要】:接下来将使用Nginx上传模块,实现文件上传及文件上传进度显示功能。用于网站文件上传,需要把安全问题考虑在内。使用Ajax能够将文件上传步骤改为无刷新操作。目前Lighttp以及Nginx都提供有相应的文件上传扩展,根据笔者的实际测试,Nginx上传扩展在效率上起码比传统的PHP脚本提高1倍。

网站文件上传多数都是使用HTTP提交实现的,与桌面程序使用Socket等上传方式不同,HTTP通信是一种异步单线程的通信方式,所以处理文件实时上传进度一直是网站开发的难题。接下来将使用Nginx上传模块,实现文件上传及文件上传进度显示功能。在这之前,首先了解目前主流的文件上传技术。

1.文件上传进度

现在多数网站实现文件上传进度显示,常用的方法有浏览器插件、Flex、Ajax、服务器上传模块等,下面分别介绍。

(1)浏览器插件

使用浏览器插件是当前大型网站所普遍采用的上传技术,国内的视频网站、QQ空间、网盘(网页版)等都是基于浏览器插件技术实现文件上传的。利用IE浏览器Acivex技术可以实现将桌面级的程序嵌入到网页中,网站就可以充分利用操作系统强大的特性,轻松地实现多线程文件上传、进度显示、文件夹上传、文件转换等。

得益于多线程处理,在上传文件过程中还能够实现队列上传、断点上传等高级功能。虽然Acivex是微软IE独有的技术,但主流浏览器都提供了扩展功能,通过安装扩展,同样能够实现类似功能。浏览器插件技术并非全是优点,也有缺点,分别如下。

➢由于浏览器插件本质上是一种桌面软件,这就意味着开发人员需要额外掌握浏览器扩展开发技术,这对于普通的PHP开发人员而言是极大的挑战。

➢对于IE浏览器而言,不能使用插件的方式实现浏览器扩展,需要使用Activex技术。出于安全考虑,通常系统已将浏览器级别调到最高,这种安全级别下操作系统是禁止自动安装Activex控件的。一些杀毒软件同样提供了相同的拦截机制。所以常见的视频网站都需要用户主动安装客户端,或者浏览器控件。对于中小型网站而言,这种用户体验是很难被认可的。

➢随着客户端浏览器或操作系统的升级,插件或Activex必须要提供相应的升级更新,这无疑增加了网站运营成本。

➢浏览器插件技术不能应用于手机平板电脑等移动终端。

(2)Flex

Flex是Flash的演进技术,与Jquery、JavaFx一样是一种富互联网技术(RAI),使用Flex技术能够轻易地创建出极具美观、动感的网站。国内的Web游戏多数都是基于Flex实现的,但由于性能问题,以及HTML 5的问世,近年来Flex的发展受到了一些挑战。

但在文件上传方面,Flex一直是最通用、主流的技术。使用Flex实现网站文件上传,不仅可以实现进度显示、多文件上传、文件夹上传等功能,而且由于充分利用操作系统网络处理功能,能够轻松地实现大文件上传、断点上传等。此外还可以结合Adobe Fms流媒体技术实现视频实时采集等高级应用。

本质上Flex也是一种浏览器插件技术,但由于Flash的普及,使用Flex技术开发的网站几乎不需要用户额外安装浏览器扩展,这对于用户而言无疑是友好的。但Flex也有缺点,归纳如下。

➢Flex是一种由Adobe掌控的技术,不像HTML、JavaScript是由W3C定义的技术,所以需要开发人员对Adobe开发技术有深入理解,例如网络通信、文件处理等。

➢Flex使用Flash Play渲染,Flash Play是一种浏览器插件,近年来多次被曝严重安全漏洞。用于网站文件上传,需要把安全问题考虑在内。

➢虽然多数浏览器都支持Flex技术,但由于性能问题,移动终端并不支持Flex。相似的技术还有JavaFx以及Silverlight等。这些技术共同点均需要额外安装浏览器扩展,虽然功能强大,但用户体验并非良好。

(3)Ajax技术

Ajax是由JavaScript、Xml、CSS等综合构成的技术,在易用性及通用性上是其最大优点。Ajax最大的亮点是能够实现网页局部无刷新操作,从而改进文件上传体验。

传统的网页文件上传需要使用表单提交到PHP处理页面,再由PHP处理表单中的文件数据。处理完成后再返回结果,开发人员需要在返回结果后将页面导航到成功处理页面。

使用Ajax能够将文件上传步骤改为无刷新操作。这一过程用户始终感觉不到曾经离开过当前页面,但事实上文件上传表单已经被后台处理完毕,并返回结果。为了让用户感觉到文件在上传,开发人员还可以使用动画图片模拟上传进度,在一定程度上提高用户体验。

由于Ajax本质上是JavaScript,所以包括移动终端在内的许多设备,都能够很好地支持Ajax。只要该设备浏览器支持文件表单域(IOS 6.0、Windows CE、Windows RT等均已支持),就可以很好地实现跨平台。所以现在很多中小型网站都广泛地使用Ajax处理文件上传。同样,Ajax也存在一些缺点,简单归纳如下。

➢Ajax虽然实现了无刷新提交表单,但本质上还是HTTP网页提交,后台的PHP脚本处理能力决定了文件上传速度,最终直接决定用户上传体验。

➢Ajax的文件上传进度是使用动画图片模拟的,事实上并不能真实反应文件上传进度,如果上传的文件比较大,这种动画模拟并不能真正有效改善用户体验。

➢Ajax通常只能上传较小的文件,例如用户头像、邮件附件等。需要注意的是,Ajax只能实现同一网站内的文件上传,不能实现跨网站上传。

(4)服务器上传模块

服务器上传模块是一种用于改进Web上传低效的技术,例如脚本运行超时、内存溢出、CPU负载过高等。服务器上传模块是一种高效的文件上传技术,它使用操作系统底层的网络处理技术,实现多线程的文件上传功能。目前Lighttp以及Nginx都提供有相应的文件上传扩展,根据笔者的实际测试,Nginx上传扩展在效率上起码比传统的PHP脚本提高1倍。

Nginx文件上传扩展,虽然不能像浏览器扩展那样能够实现文件夹上传、断点续传等高级功能。但是Nginx上传扩展支持文件集群存放(支持存放数据云)、文件实时进度显示、文件加密等功能,所以Nginx上传扩展能够适用于各类型网站文件上传的需要,并能够有效改善用户体验。接下来将重点介绍Nginx文件上传扩展功能。

2.Nginx文件上传

Nginx用于处理文件上传的扩展模块共有两个,分别为nginx_upload_module(上传文件)及nginx_uploadprogress_module(显示文件进度)。Nginx在编译时默认并没有加入这两个扩展模块,需要开发人员手动进行编译。下面首先介绍模块的安装。

(1)安装文件上传模块

默认情况下,Nginx并没有安装nginx_upload_module及nginx_uploadprogress_module模块,读者可以使用nginx–V检测是否存在该模块。

978-7-111-42852-7-Part02-584.jpg

接下来手动重新编译nginx_upload_module及nginx_uploadprogress_module模块,这里使用的操作系统为CentOS 6.0,Nginx版本为1.3.8。首先下载软件包。

978-7-111-42852-7-Part02-585.jpg

为保证安装顺利,安装前首先更新或安装Nginx依赖库。

978-7-111-42852-7-Part02-586.jpg

接着使用tar解压下载到的源代码包。

978-7-111-42852-7-Part02-587.jpg

nginx_upload_module及nginx_uploadprogress_module扩展模块不需要安装,只需要在编译Nginx时添加上即可,如果已经安装过Nginx,同样也需要重新编译。

978-7-111-42852-7-Part02-588.jpg

通过前面的步骤,接下来只需要执行make即可。

978-7-111-42852-7-Part02-589.jpg(www.chuimin.cn)

通过前面的步骤,现在nginx_upload_module及nginx_uploadprogress_module模块已经被编译进Nginx了。需要注意的是,这里只是介绍nginx_upload_module及nginx_uploadprogress_module模块安装,方便接下来的学习,如果读者全新安装Nginx(非重新编译),需要自行安装PHP及PHP-FPM,可参考本书第1章1.2.2节。

(2)配置Nginx

通过前面的步骤,现在Nginx已经支持文件上传模块,接下来只需要在配置文件中配置上传模块,即可实现文件上传。

nginx_upload_module的上传原理也是通过HTTP进行提交的,nginx_upload_module能够自动对表单中的附件进行处理,开发人员只需要提交相应的表单给Nginx即可。整个过程PHP均不直接参与文件处理,只需要负责提交数据及处理提交后的数据即可。为了方便操作,这里将在Nginx Server节点中配置节点,用于统一处理表单提交请求。如以下代码所示。

978-7-111-42852-7-Part02-590.jpg

上述代码各项配置参数含义如下:

➢upload_pass:文件上传完成处理通道。通常是一个网站,或者PHP文件地址

➢upload_store:临时文件存放路径。Nginx运行用户需要具备可读可写权限;参数1表示存放目录散列方式(Nginx将把文件随机存放到散列目录,默认为0~9)。

➢upload_store_access:存放目录访问模块。

➢upload_set_form_field:传递给upload_pass脚本的POST表单值。PHP可以使用$_POST获取到该数值。

➢upload_aggregate_form_field:变量集合。

➢upload_pass_form_field:传递给后台的参数方式。可以使用正则。

➢upload_pass_args:是否把前端脚本请求的参数传给后端处理程序。默认为on。

通过前面的配置,现在已经完成了nginx_upload_module模块的配置。要让Nginx能够实时返回上传进度,还需要配置nginx_uploadprogress_module模块。

nginx_uploadprogress_module模块运行于独立的线程,在nginx_upload_module上传文件时,能够实时地监控上传文件的状态信息。开发人员可以使用HTTP的方式获取nginx_uploadprogress_module返回的数据,这里也是通过配置Server节点实现。代码如下。

978-7-111-42852-7-Part02-591.jpg

通过上述配置,现在可以通过http://DomainName/progress初始化nginx_uploadprogress_module扩展模块。这里需要注意的是,nginx_uploadprogress_module默认是根据x-progress-id参数返回文件上传状态的,该参数值是唯一的,由上传表单通过x-progress-id参数提供。

978-7-111-42852-7-Part02-592.jpg

最终的Nginx配置信息如以下代码所示。

978-7-111-42852-7-Part02-593.jpg

978-7-111-42852-7-Part02-594.jpg

978-7-111-42852-7-Part02-595.jpg

配置完成后,可以使用/usr/local/nginx/sbin/nginx–t测试Nginx配置文件是否正确,如果配置无误,最后只需要重启或启动Nginx即可。

978-7-111-42852-7-Part02-596.jpg

(3)测试文件上传模块

通过前面的步骤,Nginx已经具备文件上传功能了。可以将表单提交给upload模块处理,一旦存在表单域,Nginx将自动上传,并由progress返回状态信息。但是如果读者直接访问http://DomainName/upload/,因为不存在表单提交,Nginx将返回404错误。这里为了便于测试,首先在网站中创建表单页面,如以下代码所示。

978-7-111-42852-7-Part02-597.jpg

如上述代码所示,其中的表单提交地址不再是PHP页面地址,而是前面配置的upload服务器模块。其中参数X-Progress-ID是文件的唯一id,获取上传进度时需要使用。前面介绍过,Nginx在上传时使用0~9的目录随机保存文件,所以在执行上传前,需要手动在/home/wwwroot/file目录中创建相应的目录。完成后就可以访问前面创建的表单页面进行文件上传了。在上传过程中,可以通过http://DomainName/progress?X-Progress-ID=xxx取得该文件的上传状态。

3.使用Ajax提交文件

通过页面的介绍,相信读者已经对Nginx的文件上传原理已经有了全面理解。Nginx在上传文件时,共有两个网址,一个用于处理表单,另一个用于返回上传状态。

根据这个原理,只需要使用Ajax异步提交表单,同时使用JavaScript时间触发事件,异步无刷新获取该文件的上传进度,并更新页面上的进度条(可以使用CSS+DIV或图创建),这样就实现了文件上传及文件上传进度实时显示。

接下来将使用Jquery提供的Ajax异步上传插件,该插件在传统的PHP上传中使用得非常广泛。如以下代码所示。

978-7-111-42852-7-Part02-598.jpg

978-7-111-42852-7-Part02-599.jpg

上述代码是普通的HTML代码,没有涉及任何PHP处理脚本,读者可以直接在MVC视图中使用。最终上传效果如图11-1所示。

978-7-111-42852-7-Part02-600.jpg

图11-1 文件实时上传进度效果

文件上传完成后,upload模块(upload_pass定义的PHP脚本输出)将返回上传文件数组信息,这里为了方便演示,只需要输出提交表单信息即可。test.php文件代码如下。

978-7-111-42852-7-Part02-601.jpg

实际应用开发中,可以将返回的数据保存到数据库或缓存系统。Nginx的上传模块从设计之初就是针对大型文件系统的,所以采用了临时目录存放的特性,在文件上传完成后,通常需要使用监控脚本将临时文件上传到文件系统(例如MooseFS、HDFS等)。在实验时,读者可以直接使用PHP将文件移动到正式目录,并且使用前面介绍的Image类库完成图片截图、创建水印等常见操作。

ˆ 说明:如果读者在本地网络中模拟上传操作,由于网速过快的原因,可能会出现stat永远为done的状态,即文件上传完成状态。解决办法是在真实的互联网中进行上传。