文章目录
- 简介
- 环境
- 服务器安装 ftp-server
- 1、查看vsftpd服务
- 2、安装 vsftpd 服务
- 3、启动服务
- 4、查看服务状态
- 5、配置账户与密码,并设定账户能允许访问的路径
- 配置boot项目
- 1、项目搭建与依赖引入
- 2、编写连接信息配置
- 3、编写测试类
简介
最近研究springboot项目通过ftp方式,进行远程服务的文件上传与下载测试,将测试中碰见的问题做一个记录汇总。
环境
- centos 8.0
- windows 10
- springboot 2.2.2.RELEASE
服务器安装 ftp-server
1、查看vsftpd服务
使用下面命令,查看服务器是否安装ftp服务
。
rpm -q vsftpd
如出现下面提示信息,则表示未安装。
2、安装 vsftpd 服务
首先需要下载,本次采取yum
的方式进行。
yum -y install vsftpd
3、启动服务
下载完成后,使用命令进行服务的启动。
systemctl start vsftpd
systemctl enable vsftpd
4、查看服务状态
systemctl status vsftpd
安装操作,参考资料:Linux的FTP服务
5、配置账户与密码,并设定账户能允许访问的路径
useradd -d 路径 账户名
如:useradd -d /etc/vsftpd/data xiangjiao
创建一个
xiangjiao
的账号,能访问的路径为/etc/vsftpd/data
。
创建完成后,需要对其设置密码:
passwd 用户名
如:passwd xiangjiao
对账号为xiangjiao
的,设置密码。
必须是 root 账号设置。
回车后,控制台会要求输入两次密码。
参考资料:Linux添加FTP用户并设置权限
配置boot项目
1、项目搭建与依赖引入
主要依赖:
<!-- ftp -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.7.2</version>
</dependency>
<!-- 工具库 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
2、编写连接信息配置
本次采取application.yml
的形式进行配置,其配置内容如下所示:
server:
port: 80
xj:
ftp:
host: xxx.xxx.xxx.xxx # 服务器地址
userName: xiangjiao
pwd: banana
port: 21
path: /
编写配置类,识别配置信息,并提供操作方法。
package com.xj.ftp.ftps;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "xj.ftp")
public class FtpConfigProperties {
// 连接超时时间设置
private static final int timeout = 1000*30;
// ftp 服务地址
private String host ;
// ftp 服务账户
private String userName;
// ftp 密码
private String pwd;
// 端口 ftp 默认 21 ,登录端口。20为传输端口,此处使用连接端口
private Integer port;
// 目录
private String path;
/**
* 创建连接
* @return
*/
public FTPClient connectFtp() {
FTPClient ftpClient = null;
try {
ftpClient = new FTPClient();
ftpClient.setConnectTimeout(timeout);
ftpClient.connect(host,port);
ftpClient.login(userName,pwd);
ftpClient.setControlEncoding("UTF-8"); //
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 二进制
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
log.info("连接FTP失败,用户名或密码错误。");
ftpClient.disconnect();
} else {
log.info("FTP连接成功!");
}
}catch (Exception e){
log.info("登陆FTP失败,请检查FTP相关配置信息是否正确!" + e);
return null;
}
return ftpClient;
}
/**
* 关闭FTP连接
*
* @param ftpClient
*/
public void closeFtpClient(FTPClient ftpClient) {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
/**
* 按前后缀查询文件
* @param facepic
* @param prefix
* @param suffix
* @return
*/
public List<String> showPatternFiles(String facepic, String prefix, String suffix) {
List<String> ret = new ArrayList();
String fileName = null;
FTPClient ftpClient = null;
try {
ftpClient = connectFtp();
boolean changeFlag = ftpClient.changeWorkingDirectory(facepic);
if (!changeFlag) {
throw new IOException("进入Ftp目录" + facepic + "失败");
}
FTPFile[] files = ftpClient.listFiles(getPath());
for (FTPFile ftpFile : files) {
fileName = ftpFile.getName();
if ((!".".equals(fileName)) && (!"..".equals(fileName))) {
String regEx = null;
if (StringUtils.isNotBlank(prefix)) {
regEx = prefix + "*." + suffix;
} else {
regEx = suffix + "$";
}
Pattern pattern = Pattern.compile(regEx);
if (pattern.matcher(fileName).find()) {
ret.add(fileName);
}
}
}
} catch (Exception e) {
log.error("获取文件失败", e);
} finally {
if (ftpClient != null) {
//close(ftpClient);
}
}
return ret;
}
/**
* 从FTP下载文件到本地
*
*/
public String downloadFile(FTPClient ftpClient, String remotePath, String fileName, String downloadPath) {
InputStream is = null;
FileOutputStream fos = null;
final File targetFile = new File(downloadPath + File.separator + fileName);
try {
is = ftpClient.retrieveFileStream(remotePath+"/"+ fileName);// 获取ftp上的文件
if(!targetFile.getParentFile().exists()){
targetFile.getParentFile().mkdirs();
}
fos = new FileOutputStream(new File(downloadPath+File.separator+fileName));
// 文件读取方式一
int i;
byte[] bytes = new byte[1024];
while ((i = is.read(bytes)) != -1) {
fos.write(bytes, 0, i);
}
// 文件读取方式二
//ftpClient.retrieveFile(fileName, new FileOutputStream(new File(downloadPath)));
ftpClient.completePendingCommand();
log.info("FTP文件下载成功!");
} catch (Exception e) {
log.error("FTP文件下载失败!",e);
} finally {
try {
if (fos != null) {
fos.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
log.error("下载流关闭失败" , e);
}
}
return targetFile.getAbsolutePath();
}
/**
* 上传文件
*
* @param serviceDec ftp服务保存地址
* @param fileName 上传到ftp的文件名
* @param originfilename 待上传文件的名称(绝对地址) *
* @return
*/
public boolean uploadFile(FTPClient ftpClient, String serviceDec, String fileName, String originfilename) {
log.info("开始上传文件");
try (InputStream input = new FileInputStream(new File(originfilename))) {
return uploadFile(ftpClient, serviceDec, fileName, input);
} catch (FileNotFoundException e) {
log.error("文件上传失败" + e);
} catch (IOException e) {
log.error("文件上传失败" + e);
}
return false;
}
/**
* 上传文件
*
* @param serviceDec ftp服务保存地址
* @param fileName 上传到ftp的文件名
* @param inputStream 输入文件流
* @return
*/
private boolean uploadFile(FTPClient ftpClient, String serviceDec, String fileName, InputStream inputStream) {
try {
log.info("开始上传文件");
ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
createDirecroty(ftpClient, serviceDec);
ftpClient.makeDirectory(serviceDec);
ftpClient.changeWorkingDirectory(serviceDec);
ftpClient.storeFile(fileName, inputStream);
inputStream.close();
ftpClient.logout();
log.info("上传文件成功");
} catch (Exception e) {
log.error("上传文件失败" + e);
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
log.error("上传文件失败" + e);
return false;
}
}
return true;
}
//改变目录路径
private boolean changeWorkingDirectory(FTPClient ftpClient, String directory) {
boolean flag = true;
try {
flag = ftpClient.changeWorkingDirectory(directory);
if (flag) {
log.info("进入文件夹" + directory + " 成功!");
} else {
log.info("进入文件夹" + directory + " 失败!开始创建文件夹");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
return flag;
}
//创建多层目录文件,如果有ftp服务器已存在该文件,则不创建,如果无,则创建
private boolean createDirecroty(FTPClient ftpClient, String remote) throws IOException {
boolean success = true;
String directory = remote + "/";
// 如果远程目录不存在,则递归创建远程服务器目录
if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(ftpClient, new String(directory))) {
int start = 0;
int end = 0;
if (directory.startsWith("/")) {
start = 1;
} else {
start = 0;
}
end = directory.indexOf("/", start);
String path = "";
String paths = "";
while (true) {
String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), "iso-8859-1");
path = path + "/" + subDirectory;
if (!existFile(ftpClient, path)) {
if (makeDirectory(ftpClient, subDirectory)) {
changeWorkingDirectory(ftpClient, subDirectory);
} else {
log.info("创建目录[" + subDirectory + "]失败");
changeWorkingDirectory(ftpClient, subDirectory);
}
} else {
changeWorkingDirectory(ftpClient, subDirectory);
}
paths = paths + "/" + subDirectory;
start = end + 1;
end = directory.indexOf("/", start);
// 检查所有目录是否创建完毕
if (end <= start) {
break;
}
}
}
return success;
}
//判断ftp服务器文件是否存在
private boolean existFile(FTPClient ftpClient, String path) throws IOException {
boolean flag = false;
FTPFile[] ftpFileArr = ftpClient.listFiles(path);
if (ftpFileArr.length > 0) {
flag = true;
}
return flag;
}
//创建目录
private boolean makeDirectory(FTPClient ftpClient, String dir) {
boolean flag = true;
try {
flag = ftpClient.makeDirectory(dir);
if (flag) {
log.info("创建文件夹" + dir + " 成功!");
} else {
log.info("创建文件夹" + dir + " 失败!");
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* 获取FTP某一特定目录下的所有文件名称
*
* @param ftpClient 已经登陆成功的FTPClient
* @param ftpDirPath FTP上的目标文件路径
*/
public List<String> getFileNameList(FTPClient ftpClient, String ftpDirPath) {
List<String> list = new ArrayList();
try {
// 通过提供的文件路径获取FTPFile对象列表
FTPFile[] files = ftpClient.listFiles(ftpDirPath);
// 遍历文件列表,打印出文件名称
for (int i = 0; i < files.length; i++) {
FTPFile ftpFile = files[i];
// 此处只打印文件,未遍历子目录(如果需要遍历,加上递归逻辑即可)
if (ftpFile.isFile()) {
// log.info(ftpDirPath + ftpFile.getName());
list.add(ftpFile.getName());
}
}
} catch (IOException e) {
log.error("错误" + e);
}
return list;
}
/**
* 获取到服务器文件夹里面最新创建的文件名称
*
* @param ftpDirPath 文件路径
* @param ftpClient ftp的连接
* @return fileName
*/
public String getNewFile(FTPClient ftpClient, String ftpDirPath) throws Exception {
// 通过提供的文件路径获取FTPFile对象列表
FTPFile[] files = ftpClient.listFiles(ftpDirPath);
if (files == null) {
throw new Exception("文件数组为空");
}
Arrays.sort(files, new Comparator<FTPFile>() {
@Override
public int compare(FTPFile f1, FTPFile f2) {
return f1.getTimestamp().compareTo(f2.getTimestamp());
}
public boolean equals(Object obj) {
return true;
}
});
return ftpDirPath + "/" + files[files.length - 1].getName();
}
}
3、编写测试类
import com.xj.ftp.ftps.FtpConfigProperties;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/ftp")
@RestController
public class FtpController {
@Autowired
private FtpConfigProperties ftpConfigProperties;
@RequestMapping("/downFtpFile")
public void downFtpFile(){
FTPClient ftpClient = ftpConfigProperties.connectFtp();
// 路径必须是对应登录账号访问权限的路径 否则会识别不到文件
// useradd -d /etc/vsftpd/data xiangjiao 创建账户 xiangjiao,并设置其访问的ftp服务路径为 /etc/vsftpd/data
// passwd xiangjiao 给账户 xiangjiao 设置密码
ftpConfigProperties.downloadFile(ftpClient,"/etc/vsftpd/data","1.txt","E:\\study\\springboot-stydy\\doc");
}
@RequestMapping("/uploadFtpFile")
public void uploadFtpFile(){
FTPClient ftpClient = ftpConfigProperties.connectFtp();
ftpConfigProperties.uploadFile(ftpClient,"/etc/vsftpd/data","2233.txt","E:\\study\\springboot-stydy\\doc\\22.txt");
}
}