目录
- 简介
- 配置 FTP
- SpringBoot 编码
- 配置 Nginx
简介
使用 SpringBoot 配置 FTP 服务器,上传、删除、下载文件。
配置 FTP
检查是否安装 vsftpd
rpm -qa | grep vsftpd
检修是否已经安装 vsftpd 及查看版本号.
安装 vsftpd
yum -y install vsftpd
如果报错,则使用管理员权限执行 sudo yum -y install vsftpd
关闭匿名访问
关闭匿名访问后,想访问里面的文件就需要账号和密码;如果不关,就可以直接访问。
vim /etc/vsftpd/vsftpd.conf
如果提示是只读文件,那么你只需要输入命令: sudo vim /etc/vsftpd/vsftpd.conf
如下:
# Example config file /etc/vsftpd/vsftpd.conf | |
# | |
# The default compiled in settings are fairly paranoid. This sample file | |
# loosens things up a bit, to make the ftp daemon more usable. | |
# Please see vsftpd.conf. for all compiled in defaults. | |
# | |
# READ THIS: This example file is NOT an exhaustive list of vsftpd options. | |
# Please read the vsftpd.conf. manual page to get a full idea of vsftpd's | |
# capabilities. | |
# | |
# Allow anonymous FTP? (Beware - allowed by default if you comment this out). | |
anonymous_enable=NO | |
# | |
# Uncomment this to allow local users to log in. | |
local_enable=YES | |
# | |
# Uncomment this to enable any form of FTP write command. | |
write_enable=YES | |
# | |
# Default umask for local users is. You may wish to change this to 022, | |
# if your users expect that ( is used by most other ftpd's) | |
local_umask= | |
# | |
# Uncomment this to allow the anonymous FTP user to upload files. This only | |
# has an effect if the above global write enable is activated. Also, you will | |
# obviously need to create a directory writable by the FTP user. | |
# When SELinux is enforcing check for SE bool allow_ftpd_anon_write, allow_ftpd_full_access | |
#anon_upload_enable=YES | |
# | |
# Uncomment this if you want the anonymous FTP user to be able to create | |
# new directories. | |
#anon_mkdir_write_enable=YES | |
# | |
# Activate directory messages - messages given to remote users when they | |
# go into a certain directory. | |
dirmessage_enable=YES | |
# | |
# Activate logging of uploads/downloads. | |
xferlog_enable=YES | |
# | |
# Make sure PORT transfer connections originate from port (ftp-data). | |
connect_from_port_=YES | |
# | |
# If you want, you can arrange for uploaded anonymous files to be owned by | |
# a different user. Note! Using "root" for uploaded files is not | |
# recommended! | |
#chown_uploads=YES | |
#chown_username=whoever | |
# | |
# You may override where the log file goes if you like. The default is shown | |
# below. | |
#xferlog_file=/var/log/xferlog | |
# | |
# If you want, you can have your log file in standard ftpd xferlog format. | |
# Note that the default log file location is /var/log/xferlog in this case. | |
xferlog_std_format=YES | |
# | |
# You may change the default value for timing out an idle session. | |
#idle_session_timeout= |
关闭匿名访问就是将:anonymous_enable=NO
启动服务
systemctl start vsftpd.service
查看服务状态
systemctl status vsftpd.service | |
[root@hadoop-master ~]# systemctl status vsftpd.service | |
● vsftpd.service - Vsftpd ftp daemon | |
Loaded: loaded (/usr/lib/systemd/system/vsftpd.service; disabled; vendor preset: disabled) | |
Active: active (running) since 一-12-19 10:15:39 CST; 58min ago | |
Process: ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf (code=exited, status=0/SUCCESS) | |
Main PID: (vsftpd) | |
CGroup: /system.slice/vsftpd.service | |
└─ /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf | |
月 19 10:15:39 hadoop-master systemd[1]: Starting Vsftpd ftp daemon... | |
月 19 10:15:39 hadoop-master systemd[1]: Started Vsftpd ftp daemon. | |
[root@hadoop-master ~]# |
看到绿色的 active(running),代表着启动成功正在运行中。
添加 FTP 用户
因为在 Linux 上,root 用户是不能登陆 FTP 的。如果你输入的是 root 用户,登陆会失败的。
adduser ftpadmin
设置密码:
passwd ftpadmin
输入两次密码就 ok 了。
配置允许root用户登录
将 /etc/vsftpd/user_list 文件和 /etc/vsftpd/ftpusers 文件中的root这一行注释掉
修改/etc/vsftpd/vsftpd.conf,在最后一行处添加local_root=/
service vsftpd restart
这样远程就可以root用户身份登录ftp了。
文件存储地址授权
如存储地址为:app/upload/,设置权限为:
chmod /app/upload/
SpringBoot 编码
添加依赖
<dependency> | |
<groupId>com.jcraft</groupId> | |
<artifactId>jsch</artifactId> | |
<version>.1.55</version> | |
</dependency> |
操作文件工具类
package com.demo.utils; | |
import com.jcraft.jsch.*; | |
import com.demo.dto.UploadFileDto; | |
import lombok.extern.slfj.Slf4j; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.util.Properties; | |
/** | |
* @ClassName: UploadFileUtils.java | |
* @Description: 上传文件 | |
* @Author: tanyp | |
* @Date:/12/19 10:38 | |
**/ | |
public class UploadFileUtils { | |
/** | |
* @MonthName: upload | |
* @Description: 上传文件 | |
* @Author: tanyp | |
* @Date:/12/19 10:38 | |
* @Param: [dto] | |
* @return: boolean | |
**/ | |
public static boolean upload(UploadFileDto dto) throws Exception { | |
log.info("============上传文件开始=============="); | |
Boolean result = false; | |
ChannelSftp sftp = null; | |
Channel channel = null; | |
Session sshSession = null; | |
try { | |
JSch jSch = new JSch(); | |
jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); | |
sshSession = jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); | |
sshSession.setPassword(dto.getPasswd()); | |
Properties sshConfig = new Properties(); | |
sshConfig.put("StrictHostKeyChecking", "no"); | |
sshSession.setConfig(sshConfig); | |
sshSession.connect(); | |
channel = sshSession.openChannel("sftp"); | |
channel.connect(); | |
sftp = (ChannelSftp) channel; | |
sftp.cd(dto.getWorkingDir()); | |
sftp.put(dto.getInputStream(), dto.getFileName()); | |
result = true; | |
log.info("============上传文件结束=============="); | |
} catch (JSchException e) { | |
result = false; | |
log.error("=====上传文件异常:{}", e.getMessage()); | |
e.printStackTrace(); | |
} finally { | |
closeChannel(sftp); | |
closeChannel(channel); | |
closeSession(sshSession); | |
} | |
return result; | |
} | |
/** | |
* @MonthName: delete | |
* @Description: 删除文件 | |
* @Author: tanyp | |
* @Date:/12/19 10:38 | |
* @Param: [dto] | |
* @return: boolean | |
**/ | |
public static boolean delete(UploadFileDto dto) throws Exception { | |
log.info("============删除文件开始=============="); | |
Boolean result = false; | |
ChannelSftp sftp = null; | |
Channel channel = null; | |
Session sshSession = null; | |
try { | |
JSch jSch = new JSch(); | |
jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); | |
sshSession = jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); | |
sshSession.setPassword(dto.getPasswd()); | |
Properties sshConfig = new Properties(); | |
sshConfig.put("StrictHostKeyChecking", "no"); | |
sshSession.setConfig(sshConfig); | |
sshSession.connect(); | |
channel = sshSession.openChannel("sftp"); | |
channel.connect(); | |
sftp = (ChannelSftp) channel; | |
sftp.cd(dto.getWorkingDir()); | |
sftp.rm(dto.getFileName()); | |
result = true; | |
log.info("============删除文件结束=============="); | |
} catch (JSchException e) { | |
result = false; | |
log.error("=====删除文件异常:{}", e.getMessage()); | |
e.printStackTrace(); | |
} finally { | |
closeChannel(sftp); | |
closeChannel(channel); | |
closeSession(sshSession); | |
} | |
return result; | |
} | |
/** | |
* @MonthName: download | |
* @Description: 下载文件 | |
* @Author: tanyp | |
* @Date:/12/19 10:38 | |
* @Param: [dto] | |
* @return: boolean | |
**/ | |
public static boolean download(UploadFileDto dto) throws Exception { | |
log.info("============下载文件开始=============="); | |
Boolean result = false; | |
ChannelSftp sftp = null; | |
Channel channel = null; | |
Session sshSession = null; | |
try { | |
JSch jSch = new JSch(); | |
jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); | |
sshSession = jSch.getSession(dto.getAccount(), dto.getHost(), dto.getPort()); | |
sshSession.setPassword(dto.getPasswd()); | |
Properties sshConfig = new Properties(); | |
sshConfig.put("StrictHostKeyChecking", "no"); | |
sshSession.setConfig(sshConfig); | |
sshSession.connect(); | |
channel = sshSession.openChannel("sftp"); | |
channel.connect(); | |
sftp = (ChannelSftp) channel; | |
sftp.cd(dto.getWorkingDir()); | |
sftp.get(dto.getFileName(), new FileOutputStream(new File(dto.getDownloadPath()))); | |
sftp.disconnect(); | |
sftp.getSession().disconnect(); | |
result = true; | |
log.info("============下载文件结束=============="); | |
} catch (JSchException e) { | |
result = false; | |
log.error("=====下载文件异常:{}", e.getMessage()); | |
e.printStackTrace(); | |
} finally { | |
closeChannel(sftp); | |
closeChannel(channel); | |
closeSession(sshSession); | |
} | |
return result; | |
} | |
private static void closeChannel(Channel channel) { | |
if (channel != null) { | |
if (channel.isConnected()) { | |
channel.disconnect(); | |
} | |
} | |
} | |
private static void closeSession(Session session) { | |
if (session != null) { | |
if (session.isConnected()) { | |
session.disconnect(); | |
} | |
} | |
} | |
} | |
UploadFileDto.java
package com.demo.dto; | |
import io.swagger.annotations.ApiModel; | |
import io.swagger.annotations.ApiModelProperty; | |
import lombok.AllArgsConstructor; | |
import lombok.Builder; | |
import lombok.Data; | |
import lombok.NoArgsConstructor; | |
import java.io.InputStream; | |
/** | |
* @ClassName: UploadFileDto.java | |
* @ClassPath: com.demo.dto.UploadFileDto.java | |
* @Description: 上传文件 | |
* @Author: tanyp | |
* @Date:/12/19 10:38 | |
**/ | |
public class UploadFileDto { | |
private String host; | |
private Integer port; | |
private String account; | |
private String passwd; | |
private String workingDir; | |
private String fileName; | |
private InputStream inputStream; | |
private String downloadPath; | |
} | |
UploadVo.java
package com.demo.vo; | |
import io.swagger.annotations.ApiModel; | |
import io.swagger.annotations.ApiModelProperty; | |
import lombok.AllArgsConstructor; | |
import lombok.Builder; | |
import lombok.Data; | |
import lombok.NoArgsConstructor; | |
/** | |
* @ClassName: UploadVo.java | |
* @ClassPath: com.demo.vo.UploadVo.java | |
* @Description: 文件VO | |
* @Author: tanyp | |
* @Date:/12/19 15:18 | |
**/ | |
public class UploadVo { | |
private String oldName; | |
private String newName; | |
private String path; | |
} | |
UploadController
package com.demo.controller; | |
import com.demo.vo.UploadVo; | |
import com.demo.service.UploadService; | |
import io.swagger.annotations.Api; | |
import io.swagger.annotations.ApiOperation; | |
import lombok.extern.slfj.Slf4j; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.web.bind.annotation.*; | |
import org.springframework.web.multipart.MultipartFile; | |
/** | |
* @ClassName: UploadController.java | |
* @ClassPath: com.demo.controller.UploadController.java | |
* @Description: 上传文件 | |
* @Author: tanyp | |
* @Date:/12/19 15:18 | |
**/ | |
public class UploadController { | |
private UploadService uploadService; | |
public UploadVo uploadImage( MultipartFile file) { | |
return uploadService.uploadImage(file); | |
} | |
public Boolean delFile(String fileName) { | |
return uploadService.delFile(fileName); | |
} | |
public Boolean downloadFile(String fileName, String downloadPath) { | |
return uploadService.downloadFile(fileName, downloadPath); | |
} | |
} | |
UploadService
package com.demo.service; | |
import com.demo.vo.UploadVo; | |
import org.springframework.web.multipart.MultipartFile; | |
/** | |
* @ClassName: UploadService.java | |
* @ClassPath: com.demo.service.UploadService.java | |
* @Description:上传文件 | |
* @Author: tanyp | |
* @Date:/12/19 15:18 | |
**/ | |
public interface UploadService { | |
UploadVo uploadImage(MultipartFile file); | |
Boolean delFile(String fileName); | |
Boolean downloadFile(String fileName, String downloadPath); | |
} |
UploadServiceImpl
package com.demo.service.impl; | |
import com.demo.dto.UploadFileDto; | |
import com.demo.vo.UploadVo; | |
import com.demo.config.FtpConfig; | |
import com.demo.service.UploadService; | |
import com.demo.utils.UUIDUtils; | |
import com.demo.utils.UploadFileUtils; | |
import com.demo.exception.BusinessException; | |
import lombok.extern.slfj.Slf4j; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.stereotype.Service; | |
import org.springframework.web.multipart.MultipartFile; | |
import java.time.LocalDate; | |
import java.time.format.DateTimeFormatter; | |
import java.util.Objects; | |
/** | |
* @ClassName: UploadServiceImpl.java | |
* @ClassPath: com.demo.service.impl.UploadServiceImpl.java | |
* @Description: 上传文件 | |
* @Author: tanyp | |
* @Date:/12/19 15:18 | |
**/ | |
public class UploadServiceImpl implements UploadService { | |
private FtpConfig ftpConfig; | |
public UploadVo uploadImage(MultipartFile file) { | |
log.info("=======上传图片开始,图片名称:{}", file.getOriginalFilename()); | |
try { | |
//. 取原始文件名 | |
String oldName = file.getOriginalFilename(); | |
//. ftp 服务器的文件名 | |
String newName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + UUIDUtils.getUUID() + oldName.substring(oldName.lastIndexOf(".")); | |
//.上传图片 | |
Boolean result = UploadFileUtils.upload( | |
UploadFileDto.builder() | |
.host(ftpConfig.host) | |
.port(ftpConfig.post) | |
.account(ftpConfig.username) | |
.passwd(ftpConfig.password) | |
.workingDir(ftpConfig.basePath) | |
.fileName(newName) | |
.inputStream(file.getInputStream()) | |
.build() | |
); | |
//.返回结果 | |
if (!result) { | |
throw new BusinessException("上传图片失败!"); | |
} | |
log.info("=======上传图片结束,新图片名称:{}", newName); | |
return UploadVo.builder() | |
.oldName(oldName) | |
.newName(newName) | |
.path(ftpConfig.imageBaseUrl + "/" + newName) | |
.build(); | |
} catch (Exception e) { | |
log.error("=======上传图片异常,异常信息:{}", e.getMessage()); | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public Boolean delFile(String fileName) { | |
if (Objects.isNull(fileName)) { | |
throw new BusinessException("文件名称为空,请核实!"); | |
} | |
try { | |
Boolean result = UploadFileUtils.delete( | |
UploadFileDto.builder() | |
.host(ftpConfig.host) | |
.port(ftpConfig.post) | |
.account(ftpConfig.username) | |
.passwd(ftpConfig.password) | |
.workingDir(ftpConfig.basePath) | |
.fileName(fileName) | |
.build() | |
); | |
return result; | |
} catch (Exception e) { | |
log.error("=======删除文件异常,异常信息:{}", e.getMessage()); | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
public Boolean downloadFile(String fileName, String downloadPath) { | |
if (Objects.isNull(fileName) || Objects.isNull(downloadPath)) { | |
throw new BusinessException("文件名称或下载路径为空,请核实!"); | |
} | |
try { | |
Boolean result = UploadFileUtils.download( | |
UploadFileDto.builder() | |
.host(ftpConfig.host) | |
.port(ftpConfig.post) | |
.account(ftpConfig.username) | |
.passwd(ftpConfig.password) | |
.workingDir(ftpConfig.basePath) | |
.fileName(fileName) | |
.downloadPath(downloadPath) | |
.build() | |
); | |
return result; | |
} catch (Exception e) { | |
log.error("=======下载文件异常,异常信息:{}", e.getMessage()); | |
e.printStackTrace(); | |
} | |
return null; | |
} | |
} | |
FtpConfig
package com.demo.config; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.stereotype.Component; | |
/** | |
* @ClassName: FtpConfig.java | |
* @ClassPath: com.demo.config.FtpConfig.java | |
* @Description: FTP配置 | |
* @Author: tanyp | |
* @Date:/12/19 22:28 | |
**/ | |
public class FtpConfig { | |
// ftp 服务器ip地址 | |
public String host; | |
// ftp 服务器port,默认是 | |
public Integer post; | |
// ftp 服务器用户名 | |
public String username; | |
// ftp 服务器密码 | |
public String password; | |
// ftp 服务器存储图片的绝对路径 | |
public String basePath; | |
// ftp 服务器外网访问图片路径 | |
public String imageBaseUrl; | |
} | |
application.yml
# ftp | |
ftp: | |
host:.0.0.1 | |
post: | |
username: ftpadmin | |
password: ftpadmin | |
base-path: /app/upload/images | |
image-base-url: http://.0.0.1:8080/images |
配置 Nginx
server { | |
listen; | |
server_name localhost; | |
location /images/ { | |
root /app/upload/; | |
autoindex on; | |
} | |
} |
SpringBoot 使用 FTP 操作文件整合完成。