哇!chrome的自动网页翻译,把这篇文章英文单词全部弄乱了,难受啊= =
本系列文章:
springboot上传下载文件(1)(项目和文件资源放在同一个服务器上)
springboot上传下载文件(2)---搭建独立的文件服务器
springboot上传下载文件(3)--java api 操作HDFS集群+集群配置
springboot上传下载文件(4)--上传下载工具类(已封装)
上一篇说道随着业务不断发展,将代码和文件放在同一服务器的弊端就会越来越明显。
为了解决上面的问题引入独立图片服务器,工作流程如下:项目上传文件时,首先通过FTP或者SSH将文件上传到图片服务器的某个目录下,再通过ngnix或者阿帕奇来访问此目录下的文件,返回一个独立域名的图片URL地址,前端使用文件时就通过这个URL地址读取。
优点:图片访问是很消耗服务器资源的(因为会涉及到操作系统的上下文切换和磁盘I / O操作),分离出来后,网络/应用服务器可以更专注发挥动态处理的能力;独立存储,更方便做扩展,容灾和数据迁移;方便做图片访问请求的负载均衡,方便应用各种缓存策略(HTTP Header,Proxy Cache等),也更加方便迁移到CDN。
缺点:单机存在性能瓶颈,容灾,垂直扩展性稍差即文件服务器挂了怎么办?
前提:
centos7
目录
1、安装FTP组件,配置FTP服务器
1.1、安装yum
1.2、通过yum安装FTP
1.3、修改配置
1.4、修改配置文件(系统用户模式)
(1)创建系统用户
(2)修改配置文件 /etc/vsftpd/vsftpd.conf
(3)关闭防火墙
(4)测试
2.2、搭建nginx的的文件服务器(centos7)
2.2.1,下载源码(Linux的的环境的只有通过源码安装.windows环境的直接解压就可以使用)
2.2.2、安装编译环境
2.2.3,安装nginx
2.2.4、启动nginx
2.2.5,测试
2.2.6,修改/usr/local/nginx/conf/nginx.conf
2.2.7,每次打开虚拟机,启动nginx的的的命令
2.3、JAVA代码(springboot)
2.3.1,添加依赖
2.3.1、修改application. properties
2.3.2、实体类FtpConfig(配置)
2.3.3、上传的工具类FtpUtil
2.3.4、工具类UploadUtils.java
2.3.5,controller层
2.3.6、测试
以root用户登录,接下来省掉许多麻烦
$apt install yum
如果报错,就apt-get update一下
$yum -y install vsftpd
用户访问模式配置
vsftpd的的服务访问模式有三种:匿名用户模式,系统用户模式和虚拟用户模式!
我这里采用系统- -用户
其他的模式请参考:https://blog.csdn.net/aiynmimi/article/details/77012507
(1)/etc/vsftpd/vsftpd.conf这个文件是vsftpd的的服务的核心配置文件!
我们在修改配置文件的时候,最好先备份一份!
$cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.older
(2)了解配置的大致内容:https ://blog.csdn.net/aiynmimi/article/details/77012507
(3)测试
Vsftpd 默认以匿名用户访问,匿名用户默认访问的FTP 服务器端路径为:/var/ftp/pub
,匿名用户只有查看权限,无法创建,删除,修改。
这种模式下,不需改动配置文件,直接启动服务即可访问!
$service vsftpd start
//或者
$systemctl start vsftpd.service
重启服务使用restart
测试1:
首先将防火墙关掉
$service firewalld stop
在cmd中输入ftp + ip地址
默认用户的FTP,密码为空
测试2:在浏览器输入FTP:// IP地址
PS:如果有蓝灯,要关掉蓝灯,否则会报错
另外默认的Vsftpd匿名用户有两个:匿名FTP,所以匿名用户如果需要上传文件,删除及修改等权限,需要FTP用户对在/ var / ftp / pub下目录有写入权限,使用如下CHOWN和CHMOD任意一种即可,设置命令如下:
chown -R ftp pub /
匿名模式可以让任何人使用ftp 服务,比较公开!多适用于共享文件!如果我们想要特定用户使用,就需要使用系统用户登录访问!这种模式,需要我们新建不同用户,linux 创建用户:
useradd ftpuser(新的用户名)
passwd ftpuser(新的用户名)
修改:
anonymous_enable=NO #禁止匿名用户登录,把默认YES改成NO
#开启被动模式
#这样远程连接才可以进行传输数据
#默认是开启的,但是要指定一个端口范围,打开vsftpd.conf文件,在后面加上
pasv_min_port=30000
pasv_max_port=30999
重新启动:
systemctl restart vsftpd
service firewalld stop
测试1
测试2
测试3:
连接成功:
发现传文件过去是报错
修改文件权限:
$cd / home / ftpuser
$chmod 777upload
PS:名为ftpuser 不能为777,但他的子目录可以为777
成功
这种模式下,登录访问的目录就是/家庭/新建用户/
自此成功!
参考:
centos7:
https://blog.csdn.net/maoyuanming0806/article/details/78067180
https://blog.csdn.net/aiynmimi/article/details/77012507
https://blog.csdn.net/csdn_lqr/article/details/53333946
nginx的的我之前有一篇文章,不过Ubuntu的的英文的,但两者差不多
官方网站:http://nginx.org/en/download.html
下载好了,移到虚拟机中
如图1所示,需要安装GCC的环境。
$yum install gcc-c++
2,第三方的开发包。
PCRE
PCRE(Perl的兼容正则表达式)是一个Perl的库,包括perl的兼容的正则表达式库.nginx的HTTP模块使用PCRE来解析正则表达式,所以需要在Linux的上安装PCRE库。
$yum install -y pcre pcre-devel
注:PCRE-devel的的是使用PCRE开发的一个二次开发库.nginx也需要此库。
zlib的的
zlib的的库提供了很多种压缩和解压缩的方式,Nginx的的使用zlib的的对HTTP包的内容进行的gzip的,所以需要在Linux的的上安装的zlib的库。
yum install -y zlib zlib-devel
OpenSSL的的
OpenSSL的是一个强大的安全套接字层密码库,囊括主要的密码算法,常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。
nginx的的不仅支持HTTP协议,还支持HTTPS(即在SSL协议上传输HTTP),所以需要在Linux的的安装的OpenSSL的库。
yum install -y openssl openssl-devel
1,把nginx的的的源码包上传到的Linux的系统
2,解压缩
tar zxf nginx-1.14.0.tar.gz
3,使用配置命令创建一Makefile文件
进入解压文件中可以看到有配置
然后进行配置生成的Makefile文件
./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi
复制上面的,粘贴在命令行,然后回车
表示成功
生成了Makefile文件
如图4所示,执行安装命令:
$make &&make install
安装成功后:
进入/ usr / local / nginx / sbin
有个nginx的的可执行文件
./nginx 及开启了服务器。
一个错误,启动是缺少文件夹:所以nginx的的的需要的文件夹必须存在,即使没有文件在其中
创建:
上边将临时文件目录指定为/ var / temp / nginx ,需要在/ var 下创建temp 及nginx 目录
[root @ localhost sbin]
$mkdir / var / temp / nginx / client -p
注意:-p
表示级联创建文件夹
自此nginx的的安装成功
最后的想要的结果是:
图片通过ftp服务上传到/ home / ftpuser / upload /目录下,我想通过访问Nginx服务器来访问ftp目录下的图片文件,该url为http:// 192.168.23.130 /picture/x.jpg。即使用HTTP请求访问原本需要使用FTP请求才能访问到的资源文件。
user root;//解决403 forbidden
server {
listen 80;
server_name 192.168.23.130;//虚拟机或服务器的ip地址
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#zj add
location /picture/ { #location指定了我们的图片服务器存放图片的地>址
root /home/ftpuser/upload; #指定图片/视频存放的地址
access_log on;
autoindex on;//打开浏览功能
}
注意:
图片是放在root / home / ftpuser / upload / picture
名为ftpuser目录权限不能为777
上传图片目录权限为777
参考:
Nginx出现403 Forbidden最终解决方法_nginx_脚本之家
https://www.jb51.net/article/121064.htm
配置完后需要重启的nginx的服务器:
./nginx -s reload
测试1:
注意最后一个“/”
测试2:
第二天打开报错:
CentOS启动nginx出现nginx:[emerg] open()“/ var / run / nginx / nginx.pid”失败(2:没有这样的文件或导演) - CSDN博客
https://blog.csdn.net/z_cc_csdn/article/details/78122282
但又出现新的问题:
linux操作系统重启后解决nginx的pid消失问题 - 陈钦 - 博客园
https://www.cnblogs.com/cyq632694540/p/6483535.html
最终解决方案:
1. 首先你要先确保关闭nginx 进程了(可以通过pkill -9 nginx 关闭进程)
2. 进入你的nginx 的安装目录里面CD` 路径/ nginx的/ sbin` 里面 然后输入侧
> nginx / sbin / nginx -c nginx / conf / nginx.conf 就可以了(sbin 目录下面有个nginx 为启动程序,-c 为以什么配置启动,后面接着nginx 配置文件的路径)
$pkill -9 nginx
$cd / usr / local / nginx / sbin
$./nginx -c conf / nginx.conf
cd / usr / local / nginx / sbin
mkdir / var / run / nginx
cd / var / run / nginx
touch nginx.conf
cd -
./nginx -c conf / nginx.conf
PS:
为什么要这样,因为每次虚拟机重启后,VAR /运行/ nginx的,nginx的这个文件夹都会被删除,搞得每一次都要去建立nginx的这个文件夹。也可以把上面写成一个SH脚本(推荐)
startingNginx.sh
#!/bin/sh
cd /usr/local/nginx/sbin
mkdir /var/run/nginx
cd /var/run/nginx
touch nginx.conf
cd -
./nginx -c conf/nginx.conf
$bash startingNginx.sh
就可以了
如果你不想像上面每次启动那么麻烦请参考:(我没有做)
https://blog.csdn.net/zyhlearnjava/article/details/71932719
如果修改配置文件后,需要重启nginx的的服务器:
./nginx -s reload
commons-fileupload
commons-fileupload
1.3.1
commons-io
commons-io
2.1
commons-net
commons-net
3.3
#ftp相关配置
FTP_ADDRESS=192.168.23.130
FTP_PORT=21
FTP_USERNAME=ftpuser
FTP_PASSWORD=123456
FTP_BASEPATH=/home/ftpuser/upload/picture
#图片服务器相关配置
IMAGE_BASE_URL=http://192.168.23.1303/picture
后来发现该配置没有起作用,故删去(其实有用,只不过后面代码我写死了)
/**
* @Auther: zj
* @Date: 2018/8/24 17:28
* @Description: 配置ftp服务器的相关参数
*/
@Component
public class FtpConfig {
/**
* 获取ip地址
*/
@Value("${FTP_ADDRESS}")
private String FTP_ADDRESS="192.168.23.130";
/**
* 端口号
*/
@Value("${FTP_PORT}")
private String FTP_PORT="21";
/**
* 用户名
*/
@Value("${FTP_USERNAME}")
private String FTP_USERNAME="ftpuser";
/**
* 密码
*/
@Value("${FTP_PASSWORD}")
private String FTP_PASSWORD="123456";
/**基本路径
*/
@Value("${FTP_BASEPATH}")
private String FTP_BASEPATH="/home/ftpuser/upload/picture";
/**
* 下载地址地基础url
*/
@Value("${IMAGE_BASE_URL}")
private String IMAGE_BASE_URL="http://192.168.23.130/picture";
public FtpConfig() {
}
public String getFTP_ADDRESS() {
return FTP_ADDRESS;
}
public void setFTP_ADDRESS(String FTP_ADDRESS) {
this.FTP_ADDRESS = FTP_ADDRESS;
}
public String getFTP_PORT() {
return FTP_PORT;
}
public void setFTP_PORT(String FTP_PORT) {
this.FTP_PORT = FTP_PORT;
}
public String getFTP_USERNAME() {
return FTP_USERNAME;
}
public void setFTP_USERNAME(String FTP_USERNAME) {
this.FTP_USERNAME = FTP_USERNAME;
}
public String getFTP_PASSWORD() {
return FTP_PASSWORD;
}
public void setFTP_PASSWORD(String FTP_PASSWORD) {
this.FTP_PASSWORD = FTP_PASSWORD;
}
public String getFTP_BASEPATH() {
return FTP_BASEPATH;
}
public void setFTP_BASEPATH(String FTP_BASEPATH) {
this.FTP_BASEPATH = FTP_BASEPATH;
}
public String getIMAGE_BASE_URL() {
return IMAGE_BASE_URL;
}
public void setIMAGE_BASE_URL(String IMAGE_BASE_URL) {
this.IMAGE_BASE_URL = IMAGE_BASE_URL;
}
}
/**
* @Auther: zj
* @Date: 2018/8/24 17:33
* @Description: 上传的工具类FtpUtil
*/
public class FtpUtil {
/**
* ftp上传文件方法
*title:pictureUpload
*@param ftpConfig 由spring管理的FtpConfig配置,在调用本方法时,可以在使用此方法的类中通过@AutoWared注入该属性。由于本方法是静态方法,所以不能在此注入该属性
*@param picNewName 图片新名称--防止重名 例如:"1.jpg"
*@param picSavePath 图片保存路径。注:最后访问路径是 ftpConfig.getFTP_ADDRESS()+"/images"+picSavePath
*@param inputStream 要上传的文件(图片)
*@return 若上传成功,返回图片的访问路径,若上传失败,返回null
* @throws IOException
*/
public static String pictureUploadByConfig(FtpConfig ftpConfig, String picNewName, String picSavePath, InputStream inputStream) throws IOException {
String picHttpPath = null;
//String picHttpPath = "";
boolean flag = uploadFile(ftpConfig.getFTP_ADDRESS(), ftpConfig.getFTP_PORT(), ftpConfig.getFTP_USERNAME(),
ftpConfig.getFTP_PASSWORD(), ftpConfig.getFTP_BASEPATH(), picSavePath, picNewName, inputStream);
if(!flag){
System.out.println( "上传失败" );
return picHttpPath;
}
//picHttpPath = ftpConfig.getFTP_ADDRESS()+"/images"+picSavePath+"/"+picNewName;
picHttpPath = ftpConfig.getIMAGE_BASE_URL()+picSavePath+"/"+picNewName;
System.out.println("上传成功==="+picHttpPath);
return picHttpPath;
}
/**
* ftp下载文件的方法
* *@param ftpConfig 由spring管理的FtpConfig配置,在调用本方法时,可以在使用此方法的类中通过@AutoWared注入该属性。由于本方法是静态方法,所以不能在此注入该属性
*@param remotePath FTP服务器上的相对路径
* @param fileName 要下载的文件名
* @param localPath 下载后保存到本地的路径
*@return 若上传成功,返回
* @throws IOException
*/
public static String pictureDowmloadByConfig(FtpConfig ftpConfig, String remotePath, String fileName, String localPath) throws IOException {
boolean flag = downloadFile(ftpConfig.getFTP_ADDRESS(),Integer.parseInt(ftpConfig.getFTP_PORT()),ftpConfig.getFTP_USERNAME(),
ftpConfig.getFTP_PASSWORD(),remotePath,fileName,localPath);
if(!flag){
System.out.println( "下载失败" );
return null;
}
System.out.println( "下载成功" );
return null;
}
/**
* Description: 向FTP服务器上传文件
* @param host FTP服务器hostname
* @param ftpPort FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param basePath FTP服务器基础目录
* @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
*/
public static boolean uploadFile(String host, String ftpPort, String username, String password, String basePath,
String filePath, String filename, InputStream input) {
int port = Integer.parseInt(ftpPort);
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);// 连接FTP服务器
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
//切换到上传目录
if (!ftp.changeWorkingDirectory(basePath+filePath)) {
//如果目录不存在创建目录
String[] dirs = filePath.split("/");
String tempPath = basePath;
for (String dir : dirs) {
if (null == dir || "".equals(dir)) continue;
tempPath += "/" + dir;
if (!ftp.changeWorkingDirectory(tempPath)) {
if (!ftp.makeDirectory(tempPath)) {
return result;
} else {
ftp.changeWorkingDirectory(tempPath);
}
}
}
}
//设置上传文件的类型为二进制类型
ftp.setFileType( FTP.BINARY_FILE_TYPE);
ftp.enterLocalPassiveMode();//这个设置允许被动连接--访问远程ftp时需要
//上传文件
if (!ftp.storeFile(filename, input)) {
return result;
}
input.close();
ftp.logout();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
//下载文件方法不用看,可能日后有用,先留在这里==========================================
/**
* Description: 从FTP服务器下载文件
* @param host FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param remotePath FTP服务器上的相对路径
* @param fileName 要下载的文件名
* @param localPath 下载后保存到本地的路径
* @return
*/
public static boolean downloadFile(String host, int port, String username, String password, String remotePath,
String fileName, String localPath) {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
File localFile = new File(localPath + "/" + ff.getName());
OutputStream is = new FileOutputStream(localFile);
ftp.retrieveFile(ff.getName(), is);
is.close();
}
}
ftp.logout();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
}
/**
* @Auther: zj
* @Date: 2018/8/24 17:40
* @Description: 上传工具类 UploadUtils
*/
public class UploadUtils {
/**
* 得到真实文件名
* @param fileName
* @return
*/
public static String subFileName(String fileName){
//查找最后一个 \ (文件分隔符)位置
int index = fileName.lastIndexOf( File.separator);
if(index == -1){
//没有分隔符,说明是真实名称
return fileName;
}else {
return fileName.substring(index+1);
}
}
/**
* 获得随机UUID文件名
* @param fileName
* @return
*/
public static String generateRandonFileName(String fileName){
//首相获得扩展名,然后生成一个UUID码作为名称,然后加上扩展名
String ext = fileName.substring(fileName.lastIndexOf("."));
return UUID.randomUUID().toString()+ext;
}
/**
* 获得hashcode 生成二级目录
* @param uuidFileName
* @return
*/
public static String generateRandomDir(String uuidFileName){
int hashCode = uuidFileName.hashCode();//得到它的hashcode编码
//一级目录
int d1 = hashCode & 0xf;
//二级目录
int d2 = (hashCode >> 4) & 0xf;
return "/"+d1+"/"+d2;
}
}
//上传头像
@PostMapping("/headImg")
public Object uploadHeadimg(@RequestParam("file") MultipartFile file) {
String username = SecurityContextHolder.getContext().getAuthentication().getName();//获取当前登录用户
System.out.println( username );
User user = userService.getUserByUsername(username);
if (!file.isEmpty()) { //文件不是空文件
try {
BufferedOutputStream out = new BufferedOutputStream(
//C:\IDEA_mode_project\agriculture\src\main
new FileOutputStream(new File(filepath + username + ".jpg")));//保存图片到目录下,建立保存文件的输入流
out.write(file.getBytes());
out.flush();
out.close();
String filename = filepath + username + ".jpg";
user.setAvater(filename); //设置头像路径
userService.saveOrUpdate(user);//修改用户信息
} catch (FileNotFoundException e) {
e.printStackTrace();
return new Reponse(false,"上传失败," + e.getMessage());
//return "上传失败," + e.getMessage(); //文件路径错误
} catch (IOException e) {
e.printStackTrace();
return new Reponse(false,"上传失败," + e.getMessage());
//return "上传失败," + e.getMessage(); //文件IO错误
}
return new Reponse(true,"上传头像成功",user);//返回用户信息
} else {
return new Reponse(false,"上传失败,因为文件是空的");
}
}
//上传文件(ftp + nginx)(多文件上传或单文件上传)
@PostMapping("/ftp/upload")
public Object uploadHeadImgNew(@RequestParam("file") MultipartFile[] files) throws IOException {
String username = SecurityContextHolder.getContext().getAuthentication().getName();//获取当前登录用户
System.out.println( username );
//User user = userService.getUserByUsername(username);
FtpConfig ftpConfig = new FtpConfig();
//System.out.println( ftpConfig.getFTP_ADDRESS() );
for (MultipartFile file : files) {
//Photo photo = new Photo();
String oldName = file.getOriginalFilename();// 获取图片原来的名字
System.out.println( oldName );
String picNewName = UploadUtils.generateRandonFileName(oldName);// 通过工具类产生新图片名称,防止重名
System.out.println( picNewName );
//String picSavePath = UploadUtils.generateRandomDir(picNewName);// 通过工具类把图片目录分级
String picSavePath = "/"+username;//以用户名为目录
System.out.println( picSavePath );
/*
* photo.setPhotoUrl(picSavePath + "/");//
* 设置图片的url--》就是存储到数据库的字符串url photo.setAlbumId(albumId);//
* 设置图片所属相册id photo.setUser_id("wk");
* photo.setPhoteName(picNewName);
*/
//photoList.add(photo);
FtpUtil.pictureUploadByConfig(ftpConfig, picNewName, picSavePath, file.getInputStream());// 上传到图片服务器的操作
// 添加到数据库
}
return new Reponse( true,"上传头像成功" );
}
/**
* 下载文件(ftp + nginx)(单个文件下载)
* @param remotePath 绝对路径或相对路径
* @param fileName 文件名
* @param localPath 下载后保存到本地的路径
* @throws IOException
* for example:
remotePath="/home/ftpuser/upload/picture/18083764688"+
fileName="33691e23-b864-4069-a83c-2427c27cdd96.jpg"+
localPath="C:\Users\zj\Desktop"
*/
@PostMapping("/ftp/download")
public void downloadHeadImgNew(@RequestParam("remotePath") String remotePath,
@RequestParam("fileName") String fileName,
@RequestParam("localPath") String localPath) throws IOException {
String username = SecurityContextHolder.getContext().getAuthentication().getName();//获取当前登录用户
System.out.println( username );
//User user = userService.getUserByUsername(username);
FtpConfig ftpConfig = new FtpConfig();
FtpUtil.pictureDowmloadByConfig( ftpConfig,remotePath,fileName,localPath );
System.out.println( "文件下载成功" );
}
用postman亲测可用
参考:
https://blog.csdn.net/qq_36762765/article/details/79539251
https://blog.csdn.net/u012149181/article/details/80608752
https://blog.csdn.net/maoyuanming0806/article/details/78068091
独立文件服务器就好了,但如果这个服务器挂了,怎么办?
下期我们来用HDFS分布式文件系统来解决这个问题
springboot上传下载文件(3)--java api 操作HDFS集群+集群配置
以及sftp的文件上传下载工具类(贼简单,可用性高):
springboot上传下载文件(4)--利用sftp封装上传下载工具类
更多精彩文章-->