封面图片

编程

使用docker和nginx搭建图片服务器详细指南

图片服务器方案有很多选项,比如选择公有的图床,或者使用像阿里云上的oss对象存储等等。这里介绍一下使用nginx搭建自有图片服务器,这样一台云服务器就可以解决程序部署,对象存储服务等多种使用场景。


安装nginx

通过yum安装nginx还是比较方便的,内网环境安装就比较麻烦,有一堆依赖需要安装。如果通过docker来安装就可以很省事。

安装过程:

1# 拉取nginx镜像 2docker pull nginx 3# 查看nginx镜像 4docker images nginx 5# 启动容器 6docker run -d --name mynginx -p 80:80 nginx:latest 7# -d 后台运行 8# --name 容器名称 9# -p 端口映射,第一个端口是宿主机端口,第二个是容器端口 10# nginx:latest 镜像名和tag标签 11 12# 查看运行的容器 13docker ps 14[root@abc nginx]# docker ps 15Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg. 16CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1719009f54bb17 docker.io/library/nginx:latest nginx -g daemon o... 5 days ago Up 44 hours ago 0.0.0.0:443->443/tcp, 0.0.0.0:11800->11800/tcp mynginx

修改配置文件的2种方式

  1. 第一种直接进入容器内部修改
1docker exec -it 190 /bin/bash
  • exec 命令代表附着到运行着的容器内部
  • it 是 -i 与 -t两个参数合并写法,-i -t 标志着为我们指定的容器创建了TTY并捕捉了STDIN
  • 190 是我们要进入的容器Id前缀
  • /bin/bash 指定了执行命令的shell

执行exit可以退出容器bash

  1. 挂载容器文件到宿主机

这种方式更方便更改配置文件,不用每次都进入容器内操作。

首先在宿主机合适位置创建nginx/conf、nginx/html和nginx/logs文件目录。

然后执行命名拷贝容器内的nginx.conf、default.conf到/nginx和/nginx/conf下。

1docker cp 190:/etc/nginx/nginx.conf ./ dokcer cp 190:/etc/nginx/conf.d/default.conf ./conf/
  • cp 命令代表复制
  • 190是我们nginx容器的ID前缀,/etc/nginx/nginx.conf 是容器内部nginx.conf 路径

此时目录格式如下:

1[root@abc nginx]# ll 2总用量 16 3drwxr-xr-x 3 root root 4096 330 21:25 conf 4drwxr-xr-x 4 root root 4096 329 19:20 html 5drwxr-xr-x 2 root root 4096 321 17:11 logs 6-rw-r--r-- 1 root root 2353 331 10:42 nginx.conf 7[root@abc nginx]# pwd 8/home/nginx

停止并删除之前启动的容器

1docker stop mynginx 2docker rm mynginx

重新执行以下命令运行新的nginx容器

1docker run -d --name mynginx -p 80:80 -v /home/nginx/nginx.conf:/etc/nginx/nginx.conf -v /home/nginx/logs:/var/log/nginx -v /home/nginx/html:/usr/share/nginx/html -v /home/nginx/conf:/etc/nginx/conf.d --privileged=true nginx:latest

在宿主机下/home/nginx/html下创建index.html就可以通过宿主机ip:80查看首页内容了。

配置图片服务器

宿主机创建目录

1/home/nginx/html/files/images

在/home/nginx/conf下创建文件file.conf,文件内容如下:

1server { 2 listen 11800; 3 server_name localhost; 4 5 location /images/ { 6 # 容器图片存放位置 7 root /usr/share/nginx/html/files; 8 autoindex on; 9 } 10}

为什么这里使用的不是80端口而是更换了其他端口?

第一,80端口一般给网页用的,如果图片服务器共用80端口,则必须暴露在公网上,非常不安全,其他人就可以直接浏览文件夹下所有内容。除非你真的想这么干。

第二,使用非80端口,安全情况下该端口都不该暴露出去,通过封装接口调用的方式来访问图片。

完成以上配置后,停止nginx容器,删除容器。在执行如下命令,增加一个新的端口11800的映射。

1docker run -d --name mynginx -p 11800:11800 -p 80:80 -v /home/nginx/nginx.conf:/etc/nginx/nginx.conf -v /home/nginx/logs:/var/log/nginx -v /home/nginx/html:/usr/share/nginx/html -v /home/nginx/conf:/etc/nginx/conf.d --privileged=true nginx:latest

到这里一个图片服务器就搭建好了。

把图片放入之前指定的/home/nginx/html/files/images下,在浏览器中就可以通过ip:11800/images/图片名访问图片了。

通过Java封装图片上传和浏览功能

通过java封装的好处是不对外直接暴露图片服务的地址,安全性高。同样的,java程序的端口也不该对外暴露,可以通过nginx的反向代理来隐藏接口信息。

上传

controller

1@PostMapping("/upload") 2public ResultJson<?> upload(@RequestPart(value = "file") MultipartFile file) { 3 return ResultJson.ok(fileService.fileUpload(file)); 4}

service

1@Service 2public class FileService { 3 @Value("${file.path.upload}") 4 private String fileSavePath; 5 @Value("${file.path.access}") 6 private String fileAccessPath; 7 8 public String fileUpload(MultipartFile upload) { 9 File file = new File(fileSavePath); 10 //判断不存在该目录就创建 11 if (!file.exists()){ 12 file.mkdirs(); 13 } 14 //获取文件名 15 String filename = upload.getOriginalFilename().replaceAll(" ", ""); 16 //起别名 17 String s = UUID.randomUUID().toString().replace("-", "").toUpperCase().substring(0, 8); 18 filename=s+filename; 19 //开始上传 20 try { 21 upload.transferTo(new File(file,filename)); 22 //返回图片访问url,可以是java接口地址,也可以是nginx代理后的地址 23 return "http://ip/images/preview/" + filename; 24 } catch (IOException e) { 25 e.printStackTrace(); 26 throw new CustomException(ResultJson.failure(ResultCode.SERVER_ERROR, "文件上传失败!")); 27 } 28 } 29}

图片预览

controller

1@GetMapping("/images/preview/{fileName}") 2public void download(@PathVariable(name = "fileName") String fileName 3 , HttpServletResponse response) throws Exception { 4 fileService.preview(fileName, response); 5}

service

1public void preview(String fileName, HttpServletResponse response) 2throws IOException { 3 URL url = new URL(fileAccessPath + fileName); 4 URLConnection conn = url.openConnection(); 5 InputStream inputStream = conn.getInputStream(); 6 7 response.reset(); 8 response.setContentType(conn.getContentType()); 9 // 在线打开方式 文件名应该编码成UTF-8 10 response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(fileName, "UTF-8")); 11 12 byte[] buffer = new byte[1024]; 13 int len; 14 OutputStream outputStream = response.getOutputStream(); 15 while ((len = inputStream.read(buffer)) > 0) { 16 outputStream.write(buffer, 0, len); 17 } 18 response.flushBuffer(); 19 inputStream.close(); 20 }

配置文件

application.yml

1file: 2 path: 3 //图片存放在宿主机上的地址 4 upload: /home/nginx/html/files/images/ 5 //nginx图片访问地址 6 access: http://localhost:11800/images/

nginx反向代理

vim conf/default.conf

在监听的80端口server下新增如下配置:

1# 上传接口返回的图片地址前缀 2location ^~ /images/preview/ { 3 # java封装的访问地址 4 proxy_pass http://localhost:14800/images/preview/; 5 6 proxy_set_header Host $proxy_host; 7 proxy_set_header X-Real-IP $remote_addr; 8 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 proxy_set_header X-NginX-Proxy true; 10 }

重启nginx

1docker restart myginx

重启完成后就可以通过ip:80/images/图片名查看图片信息了。

2023年04月05日
在初学者眼中,世界充满了可能;专家眼中,世界大都已经既定。--铃木俊隆