近来因为毕设需求,需要有一个简单的文件上传服务,结合 Nginx,最终通过 nginx-upload-module 实现。但是该模块开发者考虑到同时间同名文件上传碰撞,将文件名统一设置为一串经过计算得到的数值,交由后端服务对文件做改动。
本来不是啥大问题,只是网上教程都使用其他语言实现文件重命名,我实在不想因为这么一个简单的需求在毕设中引入其他后端语言,于是决定自写一个模块实现文件重命名功能,源码保存在 nginx-upload-rename-module 中。
毕设内容是基于 Nginx 实现流媒体点播服务器,打算自写一个支持 HLS 协议的模块,实现从 .mp4 到 .m3u8 的转码切分。正好写这个重命名模块,学习 Nginx 模块开发。
此次实验在本地机上进行,在服务器上部署基本一致。
❗ 请注意文末的提示!
思路
nginx-upload-module 的工作流程是将上传文件从 HTTP 报文中剥除,组合成文件,计算特定数值后保存到指定目录;其他文件信息则经过整合写入 HTTP 报文的请求体,交由后端处理。
我本打算直接调用它定义的变量,获取诸如文件名、文件类型等信息,直接完成文件重命名,但是可能是它变量定义时对标志位的设置,无法获取索引也没有记录入散列表,无法获得其值,就此作罢。
输出其 HTTP 报文的过程中,偶然明白了 nginx-upload-module 的 HTTP 报文传递到后端时的具体内容,于是决定对 HTTP 报文做处理,获取其特定字段,保存后用以处理文件。
编译
首先下载 Nginx 源码和两个模块:
1 | wget https://nginx.org/download/nginx-1.20.2.tar.gz |
解压缩 Nginx 源码后,有以下三个目录:
1 | . |
进入nginx-1.20.2
,准备编译。这里我直接将安装路径放在了当前目录:
自行安装 Nginx 源码编译时需要的依赖
1 | ./configure --prefix=$(pwd)/nginx --add-module=../nginx-upload-rename-module --add-module=../nginx-upload-module |
经过检查后得到以下信息:
然后执行编译:
1 | make |
编译成功后安装:
1 | make install |
这样就得到了配置文件和二进制文件:
配置
首先将nginx-upload-rename-module/UploadSuccess
目录复制入nginx/html
中。这个目录里有上传成功后返回给前端的页面。
然后修改 Nginx 的配置文件conf/nginx.conf
,在http{}
中设置以下信息,监听的端口自定,我这里就直接监听 1080 端口。
注意配置upload_store
时,指定的目录要存在,且有相应的权限,目录可以自定;client_max_body_size
决定上传文件的大小;其他配置项可以看看 ngin-upload-module 的 README。
1 | server { |
启动
设置好后,进入sbin/
,准备启动:
我没有写前端上传的页面,打算利用 Apifox 直接调用接口。
POST 请求,并插入待上传的文件,访问localhost:1080/upload
。
我这里在本地机做实验。
可以看到返回了成功界面。
实际上进行上传并调用
upload_pass
后即会返回此页面,不代表文件重命名成功,但是一般情况下都是成功的。
也可以看到指定的目录下有了对应的文件:
而如果没有启动 nginx-upload-rename-module,一般来说文件名如下,一串数字所示:
提示
因为个人能力有限,模块可能会存在 bug,已经测试过多个不同名文件同时上传,没有什么问题。
但是正如 nginx-upload-module 开发者所说,存在文件碰撞的可能,所以他们决定让用户在后端程序(如 PHP)中移动和重命名文件。我没有条件测试同一上传时间的同名文件碰撞。
但是对于同文件名但不同内容的文件 A 和文件 B,先上传文件 A,再次上传文件 B 后,会删除 文件 A 保留文件 B,即没有设置同名文件碰撞处理,请知悉。
其次,由于模块开发过程中挂载方式的选择,upload_pass
指定的location /uploadSuccess
可能无法启动其他模块的功能,只能启动本模块(暂未测试)。
所以开发的模块只适用于简单的文件上传服务,请小心使用。