FTP
服务器 (File Transfer Protocol Server) 是在互联网上提供文件存储和访问服务的计算机,它们依照 FTP协议
(文件传输协议) 提供服务。
下面将介绍其相关的 Java SDK 使用示例
一 、连接服务
1. 依赖导入
还是老规矩,首先新建 Maven
工程并导入 FTP
相关依赖,因为后续涉及到对象序列化这里引入了 Jackson
。
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
2. 创建连接
通过 FTPClient
初始化连接对象。
public void init() throws IOException {
FTPClient ftpClient = new FTPClient();
// Connect
ftpClient.connect("192.168.0.20", 21);
ftpClient.setDataTimeout(120000);
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
// failed than disconnect
ftpClient.disconnect();
}
// Login to ftp
if (ftpClient.login("budai", "123456")) {
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
// Passive connection mode
ftpClient.enterLocalPassiveMode();
// Encoding
ftpClient.setControlEncoding("GBK");
// Language code
FTPClientConfig clientConfig = new FTPClientConfig(ftpClient.getSystemType().split(" ")[0]);
clientConfig.setServerLanguageCode("zh");
ftpClient.configure(clientConfig);
} else {
System.out.println("Login to server failed");
}
}
二、目录管理
1. 创建目录
通过 makeDirectory()
方法创建新的目录。
public void create() throws IOException {
boolean isCreate = ftpClient.makeDirectory("/file/test");
System.out.println("Create success? " + isCreate);
}
2. 切换目录
通过 changeWorkingDirectory()
方法用于切换到指定目录下,同时可利用该方法判断相应目录是否存在。
public void check() throws IOException {
boolean isExist = ftpClient.changeWorkingDirectory("/file/test");
System.out.println("Path { /file/test } is exist? " + isExist);
// 切换到父路径下
ftpClient.changeToParentDirectory();
System.out.println("Change to parent, current path: " + ftpClient.printWorkingDirectory());
}
3. 删除目录
通过 removeDirectory()
方法删除已存在的目录。
public void remove() throws IOException {
boolean isDeleted = ftpClient.removeDirectory("/file/test");
System.out.println("Directory { /file/test } is deleted? " + isDeleted);
}
4. 目录监控
FTPClient
并不提供默认的目录监控接口,想要实现动态监控目录变化就需要自行实现。
实现访问也很简单,通过 listFiles()
实时获取最新的文件集合,并于旧的文件集取差集即可实现动态目录监控的效果。
public void monitorDirectory(String monitorPath) throws Exception {
List<String> fileName = Arrays.stream(ftpClient.listFiles(monitorPath))
.map(FTPFile::getName)
.collect(Collectors.toList());
System.out.println("Start monitoring directory: " + monitorPath);
while (true) {
List<String> latestList = Arrays.stream(ftpClient.listFiles(monitorPath))
.map(FTPFile::getName)
.collect(Collectors.toList());
latestList.removeAll(fileName);
if (latestList.size() > 0) {
System.out.println("Detect new file: " + latestList);
}
TimeUnit.SECONDS.sleep(2);
}
}
三、文件管理
1. 上传文件
通过 storeFile()
方法上传本地文件至服务器。
public void uploadFile() {
String remotePath = "/test/ftp-1.txt";
String localPath = "src\\main\\resources\\ftp\\ftp-1.txt";
try (InputStream in = new FileInputStream(localPath)) {
// Upload file
boolean isUpload = ftpClient.storeFile(remotePath, in);
System.out.println(isUpload);
} catch (Exception e) {
e.printStackTrace();
}
}
2. 下载文件
通过 retrieveFile()
方法下载服务器文件。
public void downloadFile() {
String remotePath = "/test/ftp-1.txt";
String localPath = "src\\main\\resources\\ftp\\ftp-1.txt";
try (OutputStream fos = new FileOutputStream(localPath)) {
// Download file
boolean isSave = ftpClient.retrieveFile(remotePath, fos);
System.out.println(isSave);
} catch (Exception e) {
e.printStackTrace();
}
}
3. 遍历文件
通过 listFiles()
方法可获取指定目录下的所有文件。
public void listFile() throws IOException {
FTPFile[] files = ftpClient.listFiles("/file");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
for (FTPFile file : files) {
String updateDate = sdf.format(file.getTimestamp().getTime());
System.out.println(file.getName() + "_" + updateDate);
}
}
4. 移动文件
移动文件一共有两种方式,最原始的方式就是先获取文件再重新上传,最后删除原目录下的文件。
当然 FTPClient
也提供更简单接口用于移动文件,即 rename()
方法,其作用效果类似 Linux
环境下的 mv 命令。
public void moveFile() {
// Manual move
try (InputStream in = ftpClient.retrieveFileStream("/test/11.txt")) {
ftpClient.storeFile("/bak/11.txt", in);
ftpClient.deleteFile("/test/11.txt");
} catch (IOException e) {
e.printStackTrace();
}
// Equal to linux "mv"
ftpClient.rename("/test/11.txt", "/bak/11.txt");
}
5. 删除文件
通过 deleteFile()
方法即可删除指定文件。
public void deleteOldFile() throws IOException {
ftpClient.changeWorkingDirectory("/file/test");
try {
ftpClient.deleteFile("hello.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
四、数据管理
1. 上传数据
在之前介绍了如何将本地文件上传至 FTP
服务器,但如果需要上传项目运行中的数据呢?
这里我用到了 GZIP
和 Jackson
工具,将需要保存的 Java
对象序列化为字符串,再保存为 GZIP
文件上传至服务器,实现代码如下:
public void uploadData() throws IOException {
String putPath = "/test/store";
String filePath = putPath + "/" + UUID.randomUUID() + ".gz";
// Compress content to gzip
User user = new User("123", "Alex", "123456");
byte[] bytes = GZIPUtil.compress(objectMapper.writeValueAsBytes(user));
// Upload file
try (InputStream in = new ByteArrayInputStream(bytes)) {
if (ftpClient.storeFile(filePath, in)) {
System.out.println("Upload file: 【" + filePath + "】 success.");
}
} catch (IOException e) {
e.printStackTrace();
}
}
2. 下载数据
下载则比较简单了,与和之前下载文件一样,然后再对下载的 GZIP
文件进行解压转化即可。
public void downloadData(String filePath) {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
// Receive ftp file
ftpClient.retrieveFile(filePath, os);
// Uncompress zip to java bean
byte[] bytes = GZIPUtil.uncompress(os.toByteArray());
User user = objectMapper.readValue(bytes, User.class);
System.out.println("Receive: " + user);
} catch (Exception e) {
e.printStackTrace();
}
}
3. GZIP工具
上述中涉及的 GZIP
数据加压缩工具方法如下:
/**
* 压缩为 GZIP 字节数组
*/
public static byte[] compress(byte[] bytes) {
try (
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(out);
) {
gzip.write(bytes);
gzip.close();
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* GZIP 解压缩
*/
public static byte[] uncompress(byte[] bytes) {
try (
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
GZIPInputStream unzip = new GZIPInputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream();
) {
byte[] buffer = new byte[1024];
int n;
while ((n = unzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}