【随笔】Java 连接操作FTP与SFTP 详细指南

引言

在Java开发中,文件传输协议(FTP)和安全文件传输协议(SFTP)是处理文件传输的两种常见方式。FTP是标准的网络文件传输协议,而SFTP则在FTP基础上增加了安全层(SSH),提供了更加安全的文件传输方式。本文将详细介绍如何在Java中实现与FTP和SFTP服务器的连接,并深入讲解各种函数的用法,以及如何进行二次封装,以提升代码的可复用性和可维护性。
Jsch官网

一、Java连接FTP的实现

FTP协议(File Transfer Protocol)是一种用于在网络中传输文件的标准协议。Java提供了丰富的库来实现FTP连接,其中最常用的是Apache Commons Net库。该库提供了一个FTPClient类,简化了与FTP服务器的交互。

1. 配置与依赖

在使用FTPClient类之前,首先需要在项目中引入Apache Commons Net库的依赖。在Maven项目中,可以通过在pom.xml文件中添加以下依赖项来实现:

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.8.0</version>
</dependency>
2. 基本连接与文件操作

使用FTPClient连接到FTP服务器的基本流程如下:

  1. 创建FTPClient对象。
  2. 使用connect方法连接到FTP服务器。
  3. 使用login方法进行身份验证。
  4. 使用相应的方法执行文件操作,如上传、下载、删除文件等。
  5. 使用logout方法注销。
  6. 最后关闭连接。

以下是一个简单的示例,展示了如何连接到FTP服务器并上传文件:

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FTPExample {
    public static void main(String[] args) {
        FTPClient ftpClient = new FTPClient();
        try {
            // 连接到FTP服务器
            ftpClient.connect("ftp.example.com", 21);
            // 登录到FTP服务器
            ftpClient.login("username", "password");

            // 设置文件传输类型为二进制文件
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

            // 上传文件
            try (InputStream inputStream = new FileInputStream("local/file/path")) {
                boolean done = ftpClient.storeFile("/remote/file/path", inputStream);
                if (done) {
                    System.out.println("The file is uploaded successfully.");
                }
            }

            // 注销并关闭连接
            ftpClient.logout();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.disconnect();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

在这个示例中,ftpClient.connect用于连接FTP服务器,ftpClient.login进行身份验证,ftpClient.storeFile方法用于上传文件。需要注意的是,FTP传输的默认模式是ASCII,因此在上传二进制文件时,需要调用setFileType(FTP.BINARY_FILE_TYPE)来设置传输模式。

3. 常用函数详解

FTPClient类提供了丰富的功能,以下是一些常用的FTP操作函数:

  • 连接与登录

    • connect(String hostname, int port):连接到指定的FTP服务器和端口。
    • login(String username, String password):使用用户名和密码登录FTP服务器。
    • logout():注销当前用户。
  • 文件操作

    • storeFile(String remote, InputStream local):将本地文件上传到服务器。
    • retrieveFile(String remote, OutputStream local):从服务器下载文件到本地。
    • deleteFile(String pathname):删除服务器上的文件。
    • listFiles(String pathname):列出指定目录下的文件和子目录。
  • 目录操作

    • makeDirectory(String pathname):在服务器上创建一个新目录。
    • removeDirectory(String pathname):删除服务器上的目录。
    • changeWorkingDirectory(String pathname):改变当前工作目录。
    • printWorkingDirectory():获取当前工作目录的路径。
  • 连接管理

    • disconnect():断开与FTP服务器的连接。
    • isConnected():检查是否已连接到服务器。
4. 二次封装的工具类

为了提高代码的可复用性,可以将上述FTP操作封装到一个工具类中。这不仅简化了使用,还可以集中处理异常和日志记录等通用逻辑。

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FTPUtil {
    private FTPClient ftpClient;

    public FTPUtil(String host, int port, String username, String password) throws IOException {
        ftpClient = new FTPClient();
        ftpClient.connect(host, port);
        ftpClient.login(username, password);
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    }

    public boolean uploadFile(String remoteFilePath, InputStream inputStream) throws IOException {
        try {
            return ftpClient.storeFile(remoteFilePath, inputStream);
        } finally {
            inputStream.close();
        }
    }

    public boolean downloadFile(String remoteFilePath, OutputStream outputStream) throws IOException {
        try {
            return ftpClient.retrieveFile(remoteFilePath, outputStream);
        } finally {
            outputStream.close();
        }
    }

    public boolean deleteFile(String filePath) throws IOException {
        return ftpClient.deleteFile(filePath);
    }

    public void logoutAndDisconnect() throws IOException {
        if (ftpClient.isConnected()) {
            ftpClient.logout();
            ftpClient.disconnect();
        }
    }
}

这个FTPUtil类提供了基本的上传、下载、删除文件功能,并封装了连接和注销逻辑,简化了FTP操作的使用。

二、Java连接SFTP的实现

SFTP(SSH File Transfer Protocol)是基于SSH协议的文件传输协议,提供了更加安全的文件传输方式。Java中实现SFTP连接通常使用JSch库,这个库由JCraft开发,用于SSH2的Java实现。

1. 配置与依赖

在使用JSch库之前,同样需要在项目中引入依赖。在Maven项目中,添加以下依赖项:

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>
2. 基本连接与文件操作

使用JSch连接到SFTP服务器的基本流程如下:

  1. 创建JSch对象。
  2. 使用getSession方法创建会话并进行身份验证。
  3. 使用connect方法连接到SFTP服务器。
  4. 使用ChannelSftp对象执行文件操作。
  5. 关闭连接。

以下是一个简单的示例,展示了如何连接到SFTP服务器并上传文件:

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class SFTPExample {
    public static void main(String[] args) {
        String SFTPHOST = "sftp.example.com";
        int SFTPPORT = 22;
        String SFTPUSER = "username";
        String SFTPPASS = "password";
        String SFTPWORKINGDIR = "/remote/path/";

        Session session = null;
        ChannelSftp channelSftp = null;

        try {
            JSch jsch = new JSch();
            session = jsch.getSession(SFTPUSER, SFTPHOST, SFTPPORT);
            session.setPassword(SFTPPASS);

            // 配置StrictHostKeyChecking属性
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            session.connect();

            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();

            // 上传文件
            try (InputStream inputStream = new FileInputStream("local/file/path")) {
                channelSftp.put(inputStream, SFTPWORKINGDIR + "fileName");
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (channelSftp != null) {
                channelSftp.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }
}

在这个示例中,JSch类用于管理SSH连接和会话,ChannelSftp类用于执行具体的SFTP操作。需要注意的是,在实际开发中,StrictHostKeyChecking属性建议设置为yes以增强安全性。

3.常用函数详解

ChannelSftp类提供了丰富的功能,用于实现SFTP操作。以下是一些常用的SFTP操作函数:

  • 连接与会话管理

    • connect():连接到SFTP服务器。
    • disconnect():断开与SFTP服务器的连接。
    • getSession():获取当前连接的会话(Session)。
  • 文件操作

    • put(InputStream src, String dst):将本地文件上传到SFTP服务器的目标路径。
    • get(String src, OutputStream dst):从SFTP服务器下载文件到本地。
    • rm(String path):删除服务器上的文件。
    • ls(String path):列出指定目录下的文件和子目录。
  • 目录操作

    • cd(String path):切换到指定目录。
    • mkdir(String path):在SFTP服务器上创建新目录。
    • rmdir(String path):删除SFTP服务器上的目录。
  • 权限与属性管理

    • chmod(int permissions, String path):修改文件或目录的权限。
    • chown(int uid, String path):修改文件或目录的所有者。
    • chgrp(int gid, String path):修改文件或目录的所属组。
    • stat(String path):获取文件或目录的属性。
4. 二次封装的工具类

为了更好地管理SFTP连接和操作,实现更强大的功能,如级联创建目录和其他操作,我们可以将常用功能封装到一个工具类中,提高代码的可读性和可维护性的同时,确保可以顺利处理复杂的文件系统操作。比如级联创建目录,在级联创建目录时,我们需要逐级检查每个目录是否存在,如果不存在则创建它。这在FTP和SFTP操作中都非常常见。

FTP工具类的级联创建目录封装
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FTPUtil {
    private FTPClient ftpClient;

    public FTPUtil(String host, int port, String username, String password) throws IOException {
        ftpClient = new FTPClient();
        ftpClient.connect(host, port);
        ftpClient.login(username, password);
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    }

    // 上传文件
    public boolean uploadFile(String remoteFilePath, InputStream inputStream) throws IOException {
        try {
            return ftpClient.storeFile(remoteFilePath, inputStream);
        } finally {
            inputStream.close();
        }
    }

    // 下载文件
    public boolean downloadFile(String remoteFilePath, OutputStream outputStream) throws IOException {
        try {
            return ftpClient.retrieveFile(remoteFilePath, outputStream);
        } finally {
            outputStream.close();
        }
    }

    // 删除文件
    public boolean deleteFile(String filePath) throws IOException {
        return ftpClient.deleteFile(filePath);
    }

    // 级联创建目录
    public boolean createDirectories(String remoteDirPath) throws IOException {
        String[] directories = remoteDirPath.split("/");
        String currentDir = "";
        boolean dirExists = false;

        for (String dir : directories) {
            if (dir.isEmpty()) continue;
            currentDir += "/" + dir;
            dirExists = ftpClient.changeWorkingDirectory(currentDir);
            if (!dirExists) {
                if (!ftpClient.makeDirectory(currentDir)) {
                    throw new IOException("Failed to create directory: " + currentDir);
                }
                ftpClient.changeWorkingDirectory(currentDir);
            }
        }
        return true;
    }

    // 删除目录(仅删除空目录)
    public boolean deleteDirectory(String directoryPath) throws IOException {
        return ftpClient.removeDirectory(directoryPath);
    }

    // 递归删除目录(包括子文件和子目录)
    public boolean deleteDirectoryRecursively(String remoteDirPath) throws IOException {
        FTPFile[] files = ftpClient.listFiles(remoteDirPath);
        for (FTPFile file : files) {
            String filePath = remoteDirPath + "/" + file.getName();
            if (file.isDirectory()) {
                deleteDirectoryRecursively(filePath);
            } else {
                deleteFile(filePath);
            }
        }
        return deleteDirectory(remoteDirPath);
    }

    // 退出并断开连接
    public void logoutAndDisconnect() throws IOException {
        if (ftpClient.isConnected()) {
            ftpClient.logout();
            ftpClient.disconnect();
        }
    }
}
SFTP工具类的级联创建目录封装
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import java.util.Vector;

public class SFTPUtil {
    private ChannelSftp channelSftp;
    private Session session;

    public SFTPUtil(String host, int port, String username, String password) throws Exception {
        JSch jsch = new JSch();
        session = jsch.getSession(username, host, port);
        session.setPassword(password);

        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.connect();

        channelSftp = (ChannelSftp) session.openChannel("sftp");
        channelSftp.connect();
    }

    // 上传文件
    public void uploadFile(String remoteDir, String remoteFileName, InputStream inputStream) throws Exception {
        channelSftp.cd(remoteDir);
        channelSftp.put(inputStream, remoteFileName);
    }

    // 下载文件
    public void downloadFile(String remoteFilePath, OutputStream outputStream) throws Exception {
        channelSftp.get(remoteFilePath, outputStream);
    }

    // 删除文件
    public void deleteFile(String remoteFilePath) throws Exception {
        channelSftp.rm(remoteFilePath);
    }

    // 级联创建目录
    public void createDirectories(String remoteDirPath) throws Exception {
        String[] directories = remoteDirPath.split("/");
        String currentDir = "";
        for (String dir : directories) {
            if (dir.isEmpty()) continue;
            currentDir += "/" + dir;
            try {
                channelSftp.cd(currentDir);
            } catch (Exception e) {
                channelSftp.mkdir(currentDir);
                channelSftp.cd(currentDir);
            }
        }
    }

    // 删除目录(仅删除空目录)
    public void deleteDirectory(String remoteDir) throws Exception {
        channelSftp.rmdir(remoteDir);
    }

    // 递归删除目录(包括子文件和子目录)
    public void deleteDirectoryRecursively(String remoteDirPath) throws Exception {
        Vector<ChannelSftp.LsEntry> files = channelSftp.ls(remoteDirPath);
        for (ChannelSftp.LsEntry entry : files) {
            if (!entry.getFilename().equals(".") && !entry.getFilename().equals("..")) {
                String filePath = remoteDirPath + "/" + entry.getFilename();
                if (entry.getAttrs().isDir()) {
                    deleteDirectoryRecursively(filePath);
                } else {
                    deleteFile(filePath);
                }
            }
        }
        deleteDirectory(remoteDirPath);
    }

    // 退出并断开连接
    public void logoutAndDisconnect() {
        if (channelSftp != null) {
            channelSftp.disconnect();
        }
        if (session != null) {
            session.disconnect();
        }
    }
}
使用注意事项
  • 级联目录创建:在处理复杂路径时,必须确保各级目录按顺序被创建。如果操作失败,应当立即停止并报告错误。
  • 递归删除:在删除目录时,递归删除可以确保目录及其所有内容都被彻底删除。然而,必须注意,这是一项危险操作,可能会导致数据丢失,因此在执行之前应该进行仔细确认。
  • 异常处理:在目录操作过程中,由于路径错误、权限不足或网络问题可能导致异常,应确保这些异常被捕获并妥善处理,以避免影响系统的稳定性。
三、Java连接FTP与SFTP的对比

FTP和SFTP在实际项目中各有应用场景。FTP由于其简单性和广泛支持,适用于一些对安全性要求不高的场景;而SFTP由于具备更高的安全性和数据加密特性,更适合用于涉及敏感数据的场景。

1. 安全性

SFTP在安全性方面具有明显的优势。它通过SSH协议加密数据传输,避免了数据在传输过程中的被窃听和篡改风险。相比之下,FTP采用明文传输,容易受到中间人攻击。

2. 性能

FTP通常在速度上略优于SFTP,因为它省去了加密和解密过程。不过,在现代硬件环境下,这种性能差异通常可以忽略不计。

3. 支持与兼容性

FTP在许多系统中得到了广泛的支持,几乎所有的操作系统和许多文件管理工具都支持FTP。而SFTP的支持相对较少,但在涉及安全要求的项目中,SFTP是更合适的选择。

四、总结与个人看法

通过本文,我们深入探讨了如何在Java中实现FTP和SFTP连接,并详细介绍了相关的函数用法和二次封装工具类的实现。对于两者的应用场景和差异,我们也进行了对比分析。

个人看法:在选择FTP还是SFTP时,应该根据项目的具体需求进行权衡。如果安全性是重中之重,SFTP无疑是首选。此外,随着互联网安全要求的日益提高,FTP的应用场景可能会逐渐减少。因此,在新项目中,建议优先考虑SFTP作为文件传输的解决方案。当然,在某些内网环境或临时性项目中,FTP的简单性和较低的配置成本也使其成为一个不错的选择。在处理文件传输任务时,封装常用的操作功能,不仅可以提高代码的复用性,还能减少开发过程中的重复劳动,提升项目的开发效率。对于级联操作和递归操作,虽然实现起来相对复杂,但这些功能对于许多实际场景是必不可少的,因此在开发中应当积极采用这些封装技术,确保系统的健壮性和灵活性。

希望这篇随笔能够帮助你更好地理解和掌握Java中FTP和SFTP的实现方法,并为实际项目提供一些有价值的参考。如果你在开发过程中有任何问题或需要进一步探讨,欢迎随时交流。

ChannelSftp是JSch库中的一个类,用于在SSH会话中执行SFTP操作。以下是ChannelSftp中的所有方法: 1. cd(String path):改变当前工作目录到指定路径。 2. chmod(int permissions, String path):修改指定路径的文件权限。 3. chown(int uid, String path):修改指定路径的文件所有者。 4. connect():连接到远程主机。 5. disconnect():关闭与远程主机的连接。 6. get(String src, OutputStream dst):将远程文件传输到本地。 7. get(String src, String dst):将远程文件传输到本地。 8. get(String src, String dst, SftpProgressMonitor monitor):将远程文件传输到本地,并提供进度监控。 9. getChannel():获取底层Channel。 10. getHome():获取远程主机的用户主目录。 11. getServerVersion():获取远程主机的SFTP版本。 12. getStat(String path):获取指定路径的文件状态。 13. isRemoteAbsolutePath(String path):判断指定路径是否为远程主机上的绝对路径。 14. lcd(String path):改变本地工作目录到指定路径。 15. ln(String oldpath, String newpath):创建一个指向指定路径的符号链接。 16. lpwd():获取本地工作目录。 17. ls(String path):获取指定路径下的文件列表。 18. lstat(String path):获取指定本地路径的文件状态。 19. mkdir(String path):创建指定路径的目录。 20. mv(String src, String dst):将指定路径的文件移动到新路径。 21. put(InputStream src, String dst):将本地文件传输到远程主机。 22. put(String src, String dst):将本地文件传输到远程主机。 23. put(String src, String dst, SftpProgressMonitor monitor):将本地文件传输到远程主机,并提供进度监控。 24. pwd():获取当前工作目录。 25. rename(String oldpath, String newpath):将指定路径的文件重命名。 26. rm(String path):删除指定路径的文件。 27. rmdir(String path):删除指定路径的目录。 28. setInputStream(InputStream in):设置输入流。 29. setOutputStream(OutputStream out):设置输出流。 30. stat(String path):获取指定路径的文件状态。 31. symlink(String oldpath, String newpath):创建一个指向指定路径的符号链接。 32. version(): 获取SFTP协议版本。 以上是ChannelSftp中的所有方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值