容器中使用ngnix搭建支持上传下载的文件服务器
文章目录
一、安装nginx容器
为了让nginx支持文件上传,需要下载并运行带有nginx-upload-module模块的容器:
1sudo podman pull docker.io/dimka2014/nginx-upload-with-progress-modules:latest
2sudo podman -d --name nginx -p 83:80 docker.io/dimka2014/nginx-upload-with-progress-modules
该容器同时带有nginx-upload-module模块和nginx-upload-progress-module模块。
注意该容器是Alpine Linux
,没有bash,有些命令与其它发行版本的Linux不一样。
使用下面的命令进入容器:
1sudo podman exec -it nginx /bin/sh
作为文件服务器, 需要显示本地时间,默认不是本地时间。通过下面一系列命令设置为本地时间:
1apk update
2apk add tzdata
3echo "Asia/Shanghai" > /etc/timezone
4rm -rf /etc/localtime
5cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
6apk del tzdata
创建文件服务器的根目录:
1mkdir -p /nginx/share
二、配置nginx
配置文件的路径为/etc/nginx/conf.d/default.conf
,作为
1server {
2 ……
3 charset utf-8; # 设置字符编码,避免中文乱码
4 location / {
5 root /nginx/share; # 根目录
6 autoindex on; # 开启索引功能
7 autoindex_exact_size off; # 关闭计算文件确切大小(单位bytes),只显示大概大小(单位kb、mb、gb)
8 autoindex_localtime on; # 显示本地时间
9 }
10}
此时我们的文件服务就配置好了,需要使用下面的命令让配置生效:
1nginx -s reload
三、支持文件上传
1. 配置nginx
上面的配置已经完成文件服务器的配置了,但是不能上传文件,想要上传文件,还需要做如下配置:
1server {
2 ……
3 charset utf-8; # 设置字符编码,避免中文乱码
4 client_max_body_size 32m;
5 upload_limit_rate 1M; # 限制上传速度最大1M
6
7 # 设置upload.html页面路由
8 location = /upload.html {
9 root /nginx; # upload.html所在路径
10 }
11
12 location /upload {
13 # 限制上传文件最大30MB
14 upload_max_file_size 30m;
15 # 设置后端处理交由@rename处理。由于nginx-upload-module模块在存储时并不是按上传的文件名存储的,所以需要自行改名。
16 upload_pass @rename;
17 # 指定上传文件存放目录,1表示按1位散列,将上传文件随机存到指定目录下的0、1、2、...、8、9目录中(这些目录要手动建立)
18 upload_store /tmp/nginx 1;
19 # 上传文件的访问权限,user:r表示用户只读,w表示可写
20 upload_store_access user:r;
21
22 # 设置传给后端处理的表单数据,包括上传的原始文件名,上传的内容类型,临时存储的路径
23 upload_set_form_field $upload_field_name.name "$upload_file_name";
24 upload_set_form_field $upload_field_name.content_type "$upload_content_type";
25 upload_set_form_field $upload_field_name.path "$upload_tmp_path";
26 upload_pass_form_field "^submit$|^description$";
27
28 # 设置上传文件的md5值和文件大小
29 upload_aggregate_form_field "${upload_field_name}_md5" "$upload_file_md5";
30 upload_aggregate_form_field "${upload_field_name}_size" "$upload_file_size";
31
32 # 如果出现下列错误码则删除上传的文件
33 upload_cleanup 400 404 499 500-505;
34 }
35
36 location @rename {
37 # 后端处理
38 proxy_pass http://localhost:81;
39 }
40}
上面的配置中,临时存储时是按1位散列来存储的,需要在上传目录下手动创建0~9几个目录。
1 mkdir -p /tmp/nginx
2 cd /tmp/nginx
3 mkdir 1 2 3 4 5 6 7 8 9 0
4 chown nginx:root . -R
2. 添加upload.html
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2<html xmlns="http://www.w3.org/1999/xhtml">
3<head>
4<title>上传</title>
5</head>
6<body>
7<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
8<form name="upload" method="POST" enctype="multipart/form-data" action="upload">
9<input type="file" name="file"/>
10<input type="submit" name="submit" value="上传"/>
11</form>
12</body>
13</html>
3. 添加后端处理服务
3.1 使用python来写后端服务
由于容器中并没有默认安装Python,要使用python作为后端服务,就需要先安装python及所需的库
1apk add python3
2pip3 install bottle
3pip3 install shutilwhich
python源码:
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
4from bottle import *
5import shutil
6
7@post("/upload")
8def postExample():
9 try:
10 dt = request.forms.dict
11 filenames = dt.get('file.name')
12 tmp_path = dt.get("file.tmp_path")
13 filepaths = dt.get("file.path")
14 count = filenames.__len__()
15 dir = os.path.abspath(filepaths[0])
16 for i in range(count):
17 print("rename %s to %s" % (tmp_path[i], os.path.join(dir, filenames[i])))
18 target = os.path.join(dir, filenames[i])
19 shutil.move(tmp_path[i], target)
20 shutil.chown(target, "nginx", "root") # 由于shutil.move不会保持用户归属,所以需要显示修改,否则访问时会报403无访问权限
21 except Exception as e:
22 print("Exception:%s" % e)
23 redirect("50x.html") # 如果是在容器中部署的nginx且映射了不同的端口,需要指定IP,端口
24 redirect('/') # 如果是在容器中部署的nginx且映射了不同的端口,需要指定IP,端口
25
26run(host='localhost', port=81)
3.2 使用go来写后端服务
如果不想在容器中额外安装东西,使用Go来作为后端服务是非常好的一个选择。只需要将源码编译成可执行文件,放到容器中运行即可。
go源码:
1package main
2
3import (
4 "fmt"
5 "net/http"
6 "os/exec"
7)
8
9func main() {
10 http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
11 err := r.ParseMultipartForm(8192)
12 if err != nil {
13 http.Redirect(w, r, "50x.html", http.StatusMovedPermanently) // 如果是在容器中部署的nginx且映射了不同的端口,需要指定IP,端口
14 return
15 }
16 filenames := r.Form.Get("file.name")
17 tmp_path := r.Form.Get("file.tmp_path")
18 filepaths := r.Form.Get("file.path")
19 target := filepaths + "/" + filenames
20 fmt.Printf("Rename:%s => %s\n", tmp_path, target)
21 cmd := exec.Command("mv", tmp_path, target) // 这里调用Linux系统的mv命令
22 _, err = cmd.Output()
23 if err != nil {
24 fmt.Printf("Rename:%s => %s Failed:%s\n", tmp_path, target, err.Error())
25 http.Redirect(w, r, "50x.html", http.StatusMovedPermanently) // 如果是在容器中部署的nginx且映射了不同的端口,需要指定IP,端口
26 return
27 }
28 http.Redirect(w, r, "/", http.StatusMovedPermanently) // 如果是在容器中部署的nginx且映射了不同的端口,需要指定IP,端口
29 })
30 fmt.Printf("Listen at 81\n")
31 http.ListenAndServe("localhost:81", nil)
32}
如果不想在Linux中安装Go环境,也可以直接在Windows下编译成Linux可执行文件,在Windows控制台输入下面的命令:
1set GOOS=linux
再编译上面的文件生成Linux下的可执行文件,然后复制到容器中,添加上执行权限,再运行。
四、获取上传进度
1.修改配置
1# 开辟一个空间proxied来存储跟踪上传的信息1MB
2upload_progress proxied 1m;
3server {
4 ……
5 location ^~ /progress {
6 # 报告上传的信息
7 report_uploads proxied;
8 }
9 location /upload {
10 ...
11 # 上传完成后,仍然保存上传信息5s
12 track_uploads proxied 5s;
13 }
14}
2. 修改上传页面upload.html
1<form id="upload" enctype="multipart/form-data" action="/upload" method="post" onsubmit="openProgressBar(); return true;">
2 <input name="file" type="file" label="fileupload" />
3 <input type="submit" value="Upload File" />
4</form>
5<div>
6 <div id="progress" style="width: 400px; border: 1px solid black">
7 <div id="progressbar" style="width: 1px; background-color: black; border: 1px solid white"> ;</div>
8 </div>
9 <div id="tp">(progress)</div>
10</div>
11<script type="text/javascript">
12 var interval = null;
13 var uuid = "";
14 function openProgressBar() {
15 for (var i = 0; i < 32; i++) {
16 uuid += Math.floor(Math.random() * 16).toString(16);
17 }
18 document.getElementById("upload").action = "/upload?X-Progress-ID=" + uuid;
19 /* 每隔一秒查询一下上传进度 */
20 interval = window.setInterval(function () {
21 fetch(uuid);
22 }, 1000);
23 }
24 function fetch(uuid) {
25 var req = new XMLHttpRequest();
26 req.open("GET", "/progress", 1);
27 req.setRequestHeader("X-Progress-ID", uuid);
28 req.onreadystatechange = function () {
29 if (req.readyState == 4) {
30 if (req.status == 200) {
31 var upload = eval(req.responseText);
32 document.getElementById('tp').innerHTML = upload.state;
33 /* 更新进度条 */
34 if (upload.state == 'done' || upload.state == 'uploading') {
35 var bar = document.getElementById('progressbar');
36 var w = 400 * upload.received / upload.size;
37 bar.style.width = w + 'px';
38 }
39 /* 上传完成,不再查询进度 */
40 if (upload.state == 'done') {
41 window.clearTimeout(interval);
42 }
43 if (upload.state == 'error') {
44 window.clearTimeout(interval);
45 alert('something wrong');
46 }
47 }
48 }
49 }
50 req.send(null);
51 }
52</script>
五、添加验证
1. 生成验证文件
1htpasswd -c .httppasswd test
这里test
是账号,密码在执行命令过程中会要求输入两次密码。
htpasswd
命令需要httpd-tools
包,如果没有可以使用下面的命令进行安装
1apt install httpd-tools # ubuntu
2yum install httpd-tools # centos
将生成的.httppasswd文件复制到容器路径 /etc/nginx/conf.d/.httppasswd
,当然也可以指定其它路径,但要求配置中的路径与之一致。
也可以直接在指定路径创建一个空文件,将.httppasswd文件的内容复制过去。
2.修改配置
1server {
2 ……
3 location / {
4 ……
5 auth_basic "download";
6 auth_basic_user_file /etc/nginx/conf.d/.httppasswd;
7 }
8}
使用nginx -s reload
让配置生效。此时访问页面就会弹出一个登录对话框,要求输入账号与密码
附上配置文件/etc/nginx/conf.d/default.conf
内容:
1# 开辟一个空间proxied来存储跟踪上传的信息1MB
2upload_progress proxied 1m;
3server {
4 listen 80;
5 server_name localhost;
6 client_max_body_size 500m;
7 charset utf-8; # 设置字符编码,避免中文乱码
8 upload_limit_rate 1M; # 限制上传速度最大1M
9
10 #charset koi8-r;
11 #access_log /var/log/nginx/log/host.access.log main;
12
13 location / {
14 root /nginx/share; # 根目录
15 autoindex on; # 开启索引功能
16 autoindex_exact_size off; # 关闭计算文件确切大小(单位bytes),只显示大概大小(单位kb、mb、gb)
17 autoindex_localtime on; # 显示本地时间
18 auth_basic "download"; # 身份验证
19 auth_basic_user_file /etc/nginx/conf.d/.httppasswd; # 身份验证文件
20 }
21
22 # 设置upload.html页面路由
23 location = /upload.html {
24 root /nginx;
25 }
26
27 location /upload {
28 # 指定上传文件存放目录,1表示按1位散列,将上传文件随机存到指定目录下的0、1、2、...、8、9目录中(这些目录要手动建立)
29 upload_store /tmp/nginx 1;
30 # 上传文件的访问权限,user:r表示用户只读,w表示可写
31 upload_store_access user:rw;
32
33 # 设置传给后端处理的表单数据,包括上传的原始文件名,上传的内容类型,临时存储的路径
34 upload_set_form_field $upload_field_name.name "$upload_file_name";
35 upload_set_form_field $upload_field_name.content_type "$upload_content_type";
36 upload_set_form_field $upload_field_name.tmp_path "$upload_tmp_path";
37 upload_set_form_field $upload_field_name.path "/nginx/share";
38 upload_pass_form_field "^submit$|^description$";
39 # 设置后端处理交由@rename处理。由于nginx-upload-module模块在存储时并不是按上传的文件名存储的,所以需要自行改名。
40 upload_pass @rename;
41
42 # 设置上传文件的md5值和文件大小
43 upload_aggregate_form_field "${upload_field_name}_md5" "$upload_file_md5";
44 upload_aggregate_form_field "${upload_field_name}_size" "$upload_file_size";
45
46 # 如果出现下列错误码则删除上传的文件
47 upload_cleanup 400 404 499 500-505;
48
49 # 上传完成后,仍然保存上传信息5s
50 track_uploads proxied 5s;
51 }
52
53 location @rename {
54 #后端处理
55 proxy_pass http://localhost:81;
56 }
57
58 # 开辟一个空间proxied来存储跟踪上传的信息1MB
59 location ^~ /progress {
60 # 报告上传的信息
61 report_uploads proxied;
62 }
63
64 #error_page 404 /404.html;
65
66 # redirect server error pages to the static page /50x.html
67 #
68 error_page 500 502 503 504 /50x.html;
69 location = /50x.html {
70 root /usr/share/nginx/html;
71 }
72
73 # proxy the PHP scripts to Apache listening on 127.0.0.1:80
74 #
75 #location ~ \.php$ {
76 # proxy_pass http://127.0.0.1;
77 #}
78
79 # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
80 #
81 #location ~ \.php$ {
82 # root html;
83 # fastcgi_pass 127.0.0.1:9000;
84 # fastcgi_index index.php;
85 # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
86 # include fastcgi_params;
87 #}
88
89 # deny access to .htaccess files, if Apache's document root
90 # concurs with nginx's one
91 #
92 #location ~ /\.ht {
93 # deny all;
94 #}
95}
六、美化页面
由于nginx的autoindex显示页面比较简陋,可以对其进行美化,可以参见笔者的另一篇博文: nginx文件服务器美化autoindex显示
参考: https://breeze2.github.io/blog/scheme-nginx-php-js-upload-process https://www.tiantanhao.com/34031.html https://blog.csdn.net/scugxl/article/details/107180138 https://octocat9lee.github.io/2020/03/11/Nginx%E6%90%AD%E5%BB%BA%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/
- 原文作者:Witton
- 原文链接:https://wittonbell.github.io/posts/2022/2022-05-10-容器中使用ngnix搭建支持上传下载的文件服务器/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。