docker部署ftp,java连接踩坑记录
下载镜像
docker pull fauria/vsftpd
启动容器
docker run -d --network=host -v /app/deploy/ftp:/home/vsftpd -e FTP_USER=ftp -e FTP_PASS=123456 --name vsftpd --restart=always fauria/vsftpd
注意此处不需要端口映射,之前我只映射了,20,21,22等端口发现,可以连接上ftp但是无法打开目录上传下载文件
使用java连接ftp
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
private static final Logger logger = LoggerFactory.getLogger(FtpServiceImpl.class);
// ip地址
@Value("${ftp.client.host}")
private String host;
// 端口号
@Value("${ftp.client.port}")
private Integer port;
// 用户名
@Value("${ftp.client.username}")
private String username;
// 密码
@Value("${ftp.client.password}")
private String password;
// session链接超时时间
@Value("${ftp.client.sessionConnectTimeout:15000}")
private Integer sessionConnectTimeout;
// channel链接超时时间
@Value("${ftp.client.channelConnectedTimeout:15000}")
private Integer channelConnectedTimeout;
/**
* 检查SFTP目录或文件是否存在
*
* @param remotePath
* @return
*/
@Override
public boolean checkFileExist(String remotePath) {
FTPClient ftpClient = loginFtp();
try {
ftpClient.changeWorkingDirectory(remotePath);
// 设置为被动模型 行内的ftp需要在这里再设置一下
ftpClient.enterLocalPassiveMode();
String[] listNames = ftpClient.listNames(remotePath);
if(listNames == null){
return false;
}
if(listNames.length > 0){
return true;
}else{
return false;
}
} catch (Exception e) {
logger.warn("检查FTP目录或文件是否存在错误: ", e);
return false;
} finally {
closeFtpClient(ftpClient);
}
}
/**
* 写远程文件
*
* @param data
* @param remoteDirPath
* @return
*/
@Override
public String writeFile(String data, String fileName, String remoteDirPath) {
return writeFile(data.getBytes(StandardCharsets.UTF_8), fileName, remoteDirPath);
}
/**
* 写远程文件
*
* @param data
* @param remoteDirPath
* @return
*/
public String writeFile(byte[] data, String fileName, String remoteDirPath) {
FTPClient ftpClient = loginFtp();
try {
boolean dirs = createDirs(remoteDirPath, ftpClient);
if (!dirs) {
logger.error("Remote path error. path:{}", remoteDirPath);
return null;
}
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.enterLocalPassiveMode();
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(data)) {
String remoteFilePath = remoteDirPath + "/" + fileName;
logger.info("upload file:::{}", remoteFilePath);
ftpClient.storeFile(remoteFilePath,inputStream);
ftpClient.logout();
return remoteFilePath;
}
} catch (IOException ex) {
logger.error("写远程文件错误", ex);
} finally {
closeFtpClient(ftpClient);
}
return null;
}
/**
* 登录FTP
*
* @return
*/
private FTPClient loginFtp() {
FTPClient ftpClient = new FTPClient();
ftpClient.setControlEncoding("UTF-8");
try {
//设置连接超时时间
ftpClient.setDataTimeout(sessionConnectTimeout);
ftpClient.setConnectTimeout(channelConnectedTimeout);
logger.info("连接FTP服务器中:" + host + ":" + port);
// 设置为被动模型
ftpClient.enterLocalPassiveMode();
//连接ftp服务器
ftpClient.connect(host, port);
//登录ftp服务器
ftpClient.login(username, password);
// 是否成功登录服务器
int replyCode = ftpClient.getReplyCode();
if (FTPReply.isPositiveCompletion(replyCode)) {
logger.info("连接FTP服务器成功:" + host + ":" + port);
} else {
logger.error("连接FTP服务器失败:" + host + ":" + port);
closeFtpClient(ftpClient);
}
} catch (IOException e) {
logger.error("连接ftp服务器异常", e);
throw new RuntimeException(e);
}
return ftpClient;
}
/**
* 关闭FTP连接
*
* @param ftpClient
*/
public void closeFtpClient(FTPClient ftpClient) {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
logger.error("关闭FTP连接异常", e);
throw new RuntimeException(e);
}
}
}
/**
* 创建FTP目录,如果目录不存在就创建
*
* @param dirPath
* @param ftpClient
* @return
*/
private boolean createDirs(String dirPath, FTPClient ftpClient) throws IOException {
if (StringUtils.isEmpty(dirPath)) {
return false;
}
if (!ftpClient.changeWorkingDirectory(dirPath)) {
boolean flag = this.changeAndMakeWorkingDir(ftpClient, dirPath);
if (!flag) {
logger.error("路径切换(创建目录)失败");
return false;
}
}
return true;
}
/**
* 路径切换(没有则创建)
*
* @param ftpClient FTP服务器
* @param path 路径
*/
public boolean changeAndMakeWorkingDir(FTPClient ftpClient, String path) {
boolean flag = false;
try {
String[] path_array = path.split("/");
for (String s : path_array) {
boolean b = ftpClient.changeWorkingDirectory(s);
if (!b) {
ftpClient.makeDirectory(s);
ftpClient.changeWorkingDirectory(s);
}
}
flag = true;
} catch (IOException e) {
logger.error("路径切换异常", e);
}
return flag;
}
容易踩坑点:
被动连接模式:ftpClient.enterLocalPassiveMode();
被动模式是ftp打开端口客户端连接该端口来传输数据,当时看了网上大部分的写法是将改行写在
//连接ftp服务器 ftpClient.connect(host, port); //登录ftp服务器 ftpClient.login(username, password);
这两行后面或中间,但是会发生报错。需要将连接模式设置到这两行代码前面(这是我docker的ftp是这样。但是行内的ftp,需要将该连接模式设置到login后面才生效估计是和ftp服务器也有一点关系吧)
主动连接模式 :ftpClient.enterLocalActiveMode();
主动连接模式是客户端开发端口提供给ftp服务器来连,如果你本地防火墙打开了。那么你会发现你能连接ftp,但是下载或上传文件后文件是空的。java默认是主动模式