简介
使用 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
如下:
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_std_format=YES
关闭匿名访问就是将:anonymous_enable=NO
启动服务
systemctl start vsftpd.service
查看服务状态
systemctl status vsftpd.service
[root@hadoop-master ~]
● vsftpd.service - Vsftpd ftp daemon
Loaded: loaded (/usr/lib/systemd/system/vsftpd.service; disabled; vendor preset: disabled)
Active: active (running) since 一 2022-12-19 10:15:39 CST; 58min ago
Process: 21702 ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf (code=exited, status=0/SUCCESS)
Main PID: 21703 (vsftpd)
CGroup: /system.slice/vsftpd.service
└─21703 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
12月 19 10:15:39 hadoop-master systemd[1]: Starting Vsftpd ftp daemon...
12月 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 777 /app/upload/
SpringBoot 编码
添加依赖
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
操作文件工具类
package com.demo.utils;
import com.jcraft.jsch.*;
import com.demo.dto.UploadFileDto;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Properties;
@Slf4j
public class UploadFileUtils {
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;
}
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;
}
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;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "上传文件Dto")
public class UploadFileDto {
@ApiModelProperty(value = " ftp 服务器ip地址")
private String host;
@ApiModelProperty(value = " ftp 服务器port,默认是21")
private Integer port;
@ApiModelProperty(value = " ftp 服务器用户名")
private String account;
@ApiModelProperty(value = " ftp 服务器密码")
private String passwd;
@ApiModelProperty(value = " ftp 服务器存储图片的绝对路径")
private String workingDir;
@ApiModelProperty(value = "上传到ftp 服务器文件名")
private String fileName;
@ApiModelProperty(value = " 文件流")
private InputStream inputStream;
@ApiModelProperty(value = " 下载文件的路径")
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;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "文件VO")
public class UploadVo {
@ApiModelProperty(value = "原始文件名称")
private String oldName;
@ApiModelProperty(value = "新文件名称")
private String newName;
@ApiModelProperty(value = "访问路径")
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.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@Slf4j
@RestController
@RequestMapping("/upload")
@Api(value = "upload", tags = "上传文件")
public class UploadController {
@Autowired
private UploadService uploadService;
@ApiOperation(value = "上传图片", notes = "上传图片")
@PostMapping("/uploadImage")
public UploadVo uploadImage(@RequestParam("file") MultipartFile file) {
return uploadService.uploadImage(file);
}
@ApiOperation(value = "删除文件", notes = "删除文件")
@GetMapping("/delFile")
public Boolean delFile(String fileName) {
return uploadService.delFile(fileName);
}
@ApiOperation(value = "下载文件", notes = "下载文件")
@GetMapping("/downloadFile")
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;
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.slf4j.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;
@Slf4j
@Service
public class UploadServiceImpl implements UploadService {
@Autowired
private FtpConfig ftpConfig;
@Override
public UploadVo uploadImage(MultipartFile file) {
log.info("=======上传图片开始,图片名称:{}", file.getOriginalFilename());
try {
String oldName = file.getOriginalFilename();
String newName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + UUIDUtils.getUUID(10) + 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;
}
@Override
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;
}
@Override
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;
@Component
public class FtpConfig {
@Value("${ftp.host}")
public String host;
@Value("${ftp.post}")
public Integer post;
@Value("${ftp.username}")
public String username;
@Value("${ftp.password}")
public String password;
@Value("${ftp.base-path}")
public String basePath;
@Value("${ftp.image-base-url}")
public String imageBaseUrl;
}
application.yml
ftp:
host: 127.0.0.1
post: 22
username: ftpadmin
password: ftpadmin
base-path: /app/upload/images
image-base-url: http://127.0.0.1:8080/images
配置 Nginx
server {
listen 8080;
server_name localhost;
location /images/ {
root /app/upload/;
autoindex on;
}
}
SpringBoot 使用 FTP 操作文件整合完成。
|