问题的抛出
就拿CSDN来说,通常我们将本地的的照片托进去就会返回一个属于这个图片的链接,然而其他人可以通过这个链接访问到我从本地拖进去的图片。
经典案例
头像的上传,从本地上传的图片,那么下次登录也能访问到。
Nginx+Docker 搭建静态资源服务器
安装docker
yum -y install docker
拉取nginx镜像
docker pull nginx
准备工作
# 创建一个存放静态资源的目录,比如我是使用了 /home/ftpadmin/health/images( 建议不要使用 root 权限,不好使用 xftp 上传文件 )
mkdir /home/ftpadmin/health/images
# 创建并编辑 nginx.conf,我的 nginx.conf 放在 /home/ftpadmin/health 路径底下
vi nginx.conf
可以在Xftp下创建,最后可以看到是这样的
修改nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /images;
index index.html index.htm;
}
}
include /etc/nginx/conf.d/*.conf;
}
执行docker实例创建命令
# --name 实例名
# -p 是端口映射,80是 nginx 容器的默认端口,映射到主机的 4545 端口,也就是访问宿主机的 4545 端口,就会自动跳转到 nginx 容器的 80 端口,也就是 nginx 的入口
# -v 是挂载目录,它会让容器和宿主机共享目录,我们把静态资源放在 /home/ftpadmin/health/images,容器内的路径 /images 也会更新静态资源,记得改成自己的真实路径
# 由于容器内没有 vim 和 vi ,甚至没有 yum ,所以我们无法在容器内修改,那么可以选择 将外部的 nginx 配置文件覆盖容器内的配置文件
挂载命令
docker run --name images_nginx -p 4545:80 -v /home/ftpadmin/health/images:/images -v /home/ftpadmin/health/nginx.conf:/etc/nginx/nginx.conf -d nginx
可以用Xftp上传几个图片
可以测试一下,图片是否可以访问啦
http://ip:4545/image.png
基于Springboot搭建图片服务器
nginx已经准备完成啦,那么现在我们要开始实现资源的上传
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.7.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- Apache工具组件 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<!-- 文件上传组件 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.3</version>
</dependency>
</dependencies>
application.yml
ftp:
host: 自己服务器ip
userName: 服务器账号
password: 服务器密码
port: 22
rootPath: /usr/nginx/image
img:
url: http://ip:4545 # ftp.img.url 可以不添加,这里只是为了上传文件成功后返回文件路径
工具类 FtpUtil.class
import com.fehead.community.error.BusinessException;
import com.fehead.community.error.EmBusinessError;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.util.Properties;
@Component
@Slf4j
public class FtpUtil {
//ftp服务器ip地址
@Value("${ftp.host}")
private String host;
//端口
@Value("${ftp.port}")
private Integer port;
//用户名
@Value("${ftp.userName}")
private String userName;
//密码
@Value("${ftp.password}")
private String password;
//存放图片的根目录
@Value("${ftp.rootPath}")
private String rootPath;
//存放图片的路径
@Value("${ftp.img.url}")
private String imgUrl;
//获取链接
private ChannelSftp getChannel() throws JSchException {
JSch jSch=new JSch();
//->ssh root@host:port
Session sshSession=jSch.getSession(userName,host,port);
//密码
sshSession.setPassword(password);
Properties sshConfig=new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
sshSession.setConfig(sshConfig);
sshSession.connect();
Channel channel=sshSession.openChannel("sftp");
channel.connect();
return (ChannelSftp) channel;
}
//ftp上传图片
public String putImages(InputStream inputStream,String imagePath,String imagesName) throws JSchException, SftpException, BusinessException {
try {
ChannelSftp sftp = getChannel();
String path = rootPath + imagePath + "/";
//创建目录
createDir(path, sftp);
//上传文件
sftp.put(inputStream, path + imagesName);
log.info("上传成功!");
sftp.quit();
sftp.exit();
//处理返回的路径
String resultFile;
resultFile = imgUrl + imagePath + imagesName;
return resultFile;
}catch (Exception e){
log.info("上传失败");
throw new BusinessException(EmBusinessError.IMAGE_INSERT_ERROR);
}
}
//创建目录
private void createDir(String path,ChannelSftp sftp) throws SftpException {
String[] folders=path.split("/");
sftp.cd("/");
for(String folder:folders){
if(folder.length()>0){
try{
sftp.cd(folder);
}catch (SftpException e){
sftp.mkdir(folder);
sftp.cd(folder);
}
}
}
}
}
工具类IDUtils.class(修改上传图片名)
import java.util.Random;
public class IDUtils {
/**
* 生成随机图片名
*/
public static String genImageName() {
//取当前时间的长整形值包含毫秒
long millis = System.currentTimeMillis();
//加上三位随机数
Random random = new Random();
int end3 = random.nextInt(999);
//如果不足三位前面补0
String str = millis + String.format("%03d", end3);
return str;
}
}
NginxService.class
import com.wzy.util.FtpUtil;
import com.wzy.util.IDUtils;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
/**
* @Package: com.wzy.service
* @Author: Clarence1
* @Date: 2019/10/4 21:34
*/
@Service
@Slf4j
public class NginxService {
public Object uploadPicture(MultipartFile uploadFile) {
//1、给上传的图片生成新的文件名
//1.1获取原始文件名
String oldName = uploadFile.getOriginalFilename();
//1.2使用IDUtils工具类生成新的文件名,新文件名 = newName + 文件后缀
String newName = IDUtils.genImageName();
assert oldName != null;
newName = newName + oldName.substring(oldName.lastIndexOf("."));
//1.3生成文件在服务器端存储的子目录
String filePath = new DateTime().toString("/yyyyMMdd/");
//2、把图片上传到图片服务器
//2.1获取上传的io流
InputStream input = null;
try {
input = uploadFile.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
//2.2调用FtpUtil工具类进行上传
return FtpUtil.putImages(input, filePath, newName);
}
}
NginxController.class
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzy.service.NginxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
@RestController
@Slf4j
public class NginxController {
@Autowired
private NginxService nginxService;
/**
* 可上传图片、视频,只需在nginx配置中配置可识别的后缀
*/
@PostMapping("/upload")
public String pictureUpload(@RequestParam(value = "file") MultipartFile uploadFile) {
long begin = System.currentTimeMillis();
String json = "";
try {
Object result = nginxService.uploadPicture(uploadFile);
json = new ObjectMapper().writeValueAsString(result);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
log.info("任务结束,共耗时:[" + (end-begin) + "]毫秒");
return json;
}
@PostMapping("/uploads")
public Object picturesUpload(@RequestParam(value = "file") MultipartFile[] uploadFile) {
long begin = System.currentTimeMillis();
Map<Object, Object> map = new HashMap<>(10);
int count = 0;
for (MultipartFile file : uploadFile) {
Object result = nginxService.uploadPicture(file);
map.put(count, result);
count++;
}
long end = System.currentTimeMillis();
log.info("任务结束,共耗时:[" + (end-begin) + "]毫秒");
return map;
}
}
PostMan测试
然后复制下方链接,就可以访问啦