我在之前的文章(Java实现文件上传和下载)里讲过非FTP文件的上传和下载,今天我们来讲一下FTP文件上传和下载,本文测试过程中Spring Boot 版本为2.5.2,commons-net 版本为3.8.0,JDK环境为 1.8。本文是在window环境下完成的,因为本机环境的复杂性,是把本机的防火墙关闭了的(不然dos登录后操作不了,或者上传下载超时)。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.2version>
<relativePath/>
parent>
<groupId>com.aliangroupId>
<artifactId>ftpartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>ftpname>
<description>java实现FTP文件上传下载description>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.12.0version>
dependency>
<dependency>
<groupId>commons-netgroupId>
<artifactId>commons-netartifactId>
<version>3.8.0version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.14version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>compilescope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
本工具类中包含四个方法,实例化方法,上传文件,下载文件,及关闭连接方法。
ApacheFtpClient.java
package com.alian.ftp.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import java.io.*;
@Slf4j
public class ApacheFtpClient {
private FTPClient ftpClient;
/**
* 实例化
*
* @param hostName FTP服务器地址
* @param port FTP服务器端口
* @param userName FTP登录账户
* @param password FTP登录密码
* @throws IOException
*/
public ApacheFtpClient(String hostName, int port, String userName, String password) throws IOException {
ftpClient = new FTPClient();
//设置传输命令的超时
ftpClient.setDefaultTimeout(20000);//毫秒
//设置两个服务连接超时时间
ftpClient.setConnectTimeout(10000);//毫秒
//被动模式下设置数据传输的超时时间
ftpClient.setDataTimeout(15000);//毫秒
//连接FTP
ftpClient.connect(hostName, port);
//更加账户密码登录服务
ftpClient.login(userName, password);
//被动模式(需要设置在连接之后,尤其linux环境)
ftpClient.enterLocalPassiveMode();
}
/**
* FTP上传文件
*
* @param remoteUploadDirectory 要上传的目录(FTP服务器上的目录)
* @param localUploadFilePathName 本地上传文件的完整路径(本地路径)
* @return
*/
public Pair<Boolean, String> uploadFile(String remoteUploadDirectory, String localUploadFilePathName) {
FileInputStream fis = null;
try {
// 如果不能进入dir下,说明此目录不存在!
System.out.println("FTP响应码:"+ftpClient.getReplyCode());
System.out.println("FTP响应信息:"+ftpClient.getReplyString());
if (!ftpClient.changeWorkingDirectory(remoteUploadDirectory)) {
log.info("没有目录:{}", remoteUploadDirectory);
if (!ftpClient.makeDirectory(remoteUploadDirectory)) {
log.info("创建文件目录【{}】 失败!", remoteUploadDirectory);
return Pair.of(false, "创建文件目录【" + remoteUploadDirectory + "】 失败");
}
}
//进入文件目录
ftpClient.changeWorkingDirectory(remoteUploadDirectory);
//创建文件流
fis = new FileInputStream(new File(localUploadFilePathName));
//设置上传目录
ftpClient.setBufferSize(1024);
//设置文件类型(二进制)
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//FTP服务器上最终的名字
String uploadFileName = localUploadFilePathName.substring(localUploadFilePathName.lastIndexOf(File.separator) + 1);
//文件上传
boolean b = ftpClient.storeFile(uploadFileName, fis);
int replyCode = ftpClient.getReplyCode();
log.info("上传文件响应码:{}", replyCode);
log.info("上传文件响应信息:{}", ftpClient.getReplyString());
return Pair.of(b, b ? "上传成功" : "上传失败");
} catch (Exception e) {
log.error("FTP上传文件异常!:", e);
return Pair.of(false, "上传文件异常");
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
log.error("关闭流发生异常!", e);
}
}
}
/**
* FTP文件下载
*
* @param remoteDownloadDirectory 要下载的目录(FTP服务器目录)
* @param localDirectory 本地下载文件路径
* @param downloadFileName 下载的文件名
* @return
*/
public Pair<Boolean, String> downloadFile(String remoteDownloadDirectory, String localDirectory, String downloadFileName) {
OutputStream out = null;
try {
if (StringUtils.isBlank(downloadFileName)) {
return Pair.of(false, "要下载的文件不能为空");
}
//工作目录切换到下载文件的目录下
if (!ftpClient.changeWorkingDirectory(remoteDownloadDirectory)) {
log.info("目录不存在:{}", remoteDownloadDirectory);
return Pair.of(false, "目录不存在");
}
//获取目录下所有文件
FTPFile[] files = ftpClient.listFiles();
if (files.length < 1) {
return Pair.of(false, "目录为空");
}
boolean fileExist = false;
boolean downloadFlag = false;
//遍历文件列表
for (FTPFile ftpFile : files) {
String localFile = localDirectory + File.separator + downloadFileName;
//是否存在要下载的文件
if (downloadFileName.equals(ftpFile.getName())) {
fileExist = true;
out = new FileOutputStream(localFile);
//下载
downloadFlag = ftpClient.retrieveFile(downloadFileName, out);
int replyCode = ftpClient.getReplyCode();
log.info("下载文件响应码:{}", replyCode);
break;
}
}
if (!fileExist) {
return Pair.of(false, "FTP服务器上文件不存在");
}
return Pair.of(downloadFlag, downloadFlag ? "下载成功" : "下载失败");
} catch (Exception e) {
log.error("FTP下载文件异常!:", e);
return Pair.of(false, "下载文件异常");
} finally {
try {
if (out != null) {
out.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void close() {
try {
if (ftpClient != null && ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意构造方法中的调用,FTP协议有两种工作方式:
// 被动模式(需要设置在连接之后,尤其linux环境)
ftpClient.enterLocalPassiveMode();
如果是windows环境上传下载没啥问题,如果是linux环境,可能端口未开放就需要我们使用这个被动模式,而且是登录之后,不然你调用 listFiles() 方法可能就是返回null了。
如果你是要设置编码的话,你需要在你连接,登录之前进行设置。不然就是无效,FTP服务器默认的编码是 ISO-8859-1 。所以你上传和下载时,不管是文件目录,或者文件名,或者文件的编码都要进行相应的统一转换,我这里就没有改服务器上编码格式了。
//被动模式(需要设置在连接之前)
ftpClient.setControlEncoding("UTF-8");
主动模式的FTP是指服务器主动连接客户端的数据端口。
被动模式的FTP是指服务器被动地等待客户端连接自己的数据端口。
如果你是window环境下操作FTP,建议关闭防火墙操作。
输入命令:
#在cmd命令提示符下输入(ftp加FTP服务器地址)
ftp 192.168.0.151
#根据提示输入用户名
Alian
#根据提示输入登录密码
Alian@1223
@Test
public void upload() throws IOException {
ApacheFtpClient apacheFtpClient = new ApacheFtpClient("192.168.0.151", 21, "Alian", "Alian@1223");
Pair<Boolean, String> pair = apacheFtpClient.uploadFile("apacheFTP", "C:\\myFile\\CSDN\\result.png",);
log.info("上传返回结果:{}", pair);
apacheFtpClient.close();
}
运行结果:
17:16:55.830 [main] INFO com.alian.ftp.utils.ApacheFtpClient - 上传文件响应码:226
17:16:55.835 [main] INFO com.alian.ftp.service.TestApacheFtpService - 上传返回结果:(true,上传成功)
@Test
public void download() throws IOException {
ApacheFtpClient apacheFtpClient = new ApacheFtpClient("192.168.0.151", 21, "Alian", "Alian@1223");
Pair<Boolean, String> pair = apacheFtpClient.downloadFile("apacheFTP", "C:\\myFile\\download", "result.png");
log.info("下载返回结果:{}", pair);
apacheFtpClient.close();
}
运行结果:
17:20:37.126 [main] INFO com.alian.ftp.utils.ApacheFtpClient - 下载文件响应码:226
17:20:37.130 [main] INFO com.alian.ftp.service.TestApacheFtpService - 下载返回结果:(true,下载成功)