Java实现FTP文件上传和下载

发布时间 2023-12-04 15:32:10作者: 磁力搜索专家

目录
一、背景
二、maven依赖
三、FTP工具类
3.1、主动模式(PORT)
3.2、被动模式(PASV)
四、验证
4.1、dos下操作FTP
4.2、FTP文件上传
4.3、FTP文件下载
一、背景
  我在之前的文章(Java实现文件上传和下载)里讲过非FTP文件的上传和下载,今天我们来讲一下FTP文件上传和下载,本文测试过程中Spring Boot 版本为2.5.2,commons-net 版本为3.8.0,JDK环境为 1.8。本文是在window环境下完成的,因为本机环境的复杂性,是把本机的防火墙关闭了的(不然dos登录后操作不了,或者上传下载超时)。

二、maven依赖
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>com.alian</groupId>
<artifactId>ftp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ftp</name>
<description>java实现FTP文件上传下载</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>

<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
三、FTP工具类
  本工具类中包含四个方法,实例化方法,上传文件,下载文件,及关闭连接方法。

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();
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  注意构造方法中的调用,FTP协议有两种工作方式:

// 被动模式(需要设置在连接之后,尤其linux环境)
ftpClient.enterLocalPassiveMode();
1
2
  如果是windows环境上传下载没啥问题,如果是linux环境,可能端口未开放就需要我们使用这个被动模式,而且是登录之后,不然你调用 listFiles() 方法可能就是返回null了。

  如果你是要设置编码的话,你需要在你连接,登录之前进行设置。不然就是无效,FTP服务器默认的编码是 ISO-8859-1 。所以你上传和下载时,不管是文件目录,或者文件名,或者文件的编码都要进行相应的统一转换,我这里就没有改服务器上编码格式了。

//被动模式(需要设置在连接之前)
ftpClient.setControlEncoding("UTF-8");
1
2
3.1、主动模式(PORT)
  主动模式的FTP是指服务器主动连接客户端的数据端口。

FTP客户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,同时开放N+1号端口进行监听,然后发送PORT命令到FTP服务器,告知是主动模式
FTP服务器给客户端的命令端口返回一个ACK的应答码
FTP客户端发出数据传输的指令
FTP服务器发起一个从它自己的数据端口(一般是20端口)到客户端先前指定的数据端口(N+1)的连接
FTP客户端返回一个ACK后,进行数据传输
3.2、被动模式(PASV)
  被动模式的FTP是指服务器被动地等待客户端连接自己的数据端口。

FTP客户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,同时会开启N+1号端口。然后向服务器发送PASV命令,告知是被动模式
FTP服务器收到命令后,会开放一个大于1024的端口P进行监听,然后用PORT命令通知客户端,服务端的数据端口是P
FTP客户端发出数据传输的指令,会通过N+1号数据端口连接服务器的端口P
FTP服务器给客户端的数据端口返回一个ACK的应答码,然后在两个端口之间进行数据传输
四、验证
4.1、dos下操作FTP
  如果你是window环境下操作FTP,建议关闭防火墙操作。
输入命令:

#在cmd命令提示符下输入(ftp加FTP服务器地址)
ftp 192.168.0.151
#根据提示输入用户名
Alian
#根据提示输入登录密码
Alian@1223
1
2
3
4
5
6
本文登录后的文件列表:


4.2、FTP文件上传
@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();
}
1
2
3
4
5
6
7
运行结果:

17:16:55.830 [main] INFO com.alian.ftp.utils.ApacheFtpClient - 上传文件响应码:226
17:16:55.835 [main] INFO com.alian.ftp.service.TestApacheFtpService - 上传返回结果:(true,上传成功)
1
2
登录FTP服务器上查看文件如下:


4.3、FTP文件下载
@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();
}
1
2
3
4
5
6
7
运行结果:

17:20:37.126 [main] INFO com.alian.ftp.utils.ApacheFtpClient - 下载文件响应码:226
17:20:37.130 [main] INFO com.alian.ftp.service.TestApacheFtpService - 下载返回结果:(true,下载成功)
1
2
进入到我们本地下载的目录,结果如下:

————————————————
版权声明:本文为CSDN博主「嘉禾嘉宁papa」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Alian_1223/article/details/121315158