杰瑞科技汇

Java如何用Apache FTP实现文件传输?

Of course! Using Java to work with FTP servers is a common task. The most popular and robust library for this is Apache Commons Net.

Java如何用Apache FTP实现文件传输?-图1
(图片来源网络,侵删)

Here's a comprehensive guide covering everything you need to know, from setup to practical examples.


What is Apache Commons Net?

It's a Java library from the Apache Software Foundation that provides a collection of network protocols, including:

  • FTP (File Transfer Protocol)
  • FTPS (FTP over SSL/TLS)
  • SFTP (SSH File Transfer Protocol) - Note: SFTP is handled by a different library, JSch, not Commons Net.
  • SMTP, POP3, NNTP, etc.

For FTP, it provides the FTPClient class, which is powerful, flexible, and handles all the low-level details of the FTP protocol.


Setup: Adding the Dependency

You need to add the Apache Commons Net library to your project.

Java如何用Apache FTP实现文件传输?-图2
(图片来源网络,侵删)

For Maven (pom.xml)

Add this dependency to your pom.xml file:

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.9.0</version> <!-- Check for the latest version -->
</dependency>

For Gradle (build.gradle)

Add this line to your dependencies block in build.gradle:

implementation 'commons-net:commons-net:3.9.0' // Check for the latest version

Core FTPClient Concepts

Before diving into code, understand these key concepts:

  • FTPClient: The main class you'll interact with.
  • FTPClientConfig: Used to configure the client for specific FTP server types (e.g., Unix, Windows, IBM OS/400). This helps with parsing directory listings correctly.
  • Connection Modes:
    • Active Mode: The server initiates a data connection back to the client. This can be problematic if the client is behind a firewall (NAT).
    • Passive Mode (Recommended): The client initiates both the control and data connections. This is generally more firewall-friendly. You should almost always use passive mode.
  • File Types:
    • FTP.ASCII_FILE_TYPE: For text files. The client may perform character conversions (e.g., newline characters).
    • FTP.BINARY_FILE_TYPE: For non-text files (images, videos, archives). This is the most common and safest choice.
  • Return Codes: Almost every method in FTPClient returns an integer. A value of 0 or positive usually indicates success (following the FTP standard), while a negative value indicates an error. You should always check the return code.

Practical Code Examples

Here are the most common FTP operations. Each example is a self-contained method.

Example 1: Connecting and Logging In

This is the first step for any FTP operation.

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import java.io.IOException;
public class FtpConnectionExample {
    public static void main(String[] args) {
        String server = "ftp.example.com";
        int port = 21;
        String user = "username";
        String pass = "password";
        FTPClient ftpClient = new FTPClient();
        try {
            // 1. Connect to the server
            System.out.println("Connecting to " + server);
            ftpClient.connect(server, port);
            int replyCode = ftpClient.getReplyCode();
            // Check if connection was successful
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                System.out.println("FTP server refused connection.");
                return;
            }
            // 2. Login to the server
            System.out.println("Logging in as " + user);
            boolean loginSuccess = ftpClient.login(user, pass);
            if (!loginSuccess) {
                System.out.println("Login failed. Check username and password.");
                ftpClient.disconnect();
                return;
            }
            System.out.println("Connection and login successful.");
            // ... perform other operations here ...
        } catch (IOException ex) {
            System.out.println("Error: " + ex.getMessage());
            ex.printStackTrace();
        } finally {
            try {
                // 3. Logout and disconnect
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                    System.out.println("Disconnected from the server.");
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Example 2: Uploading a File (Upload)

This example uploads a local file to the FTP server in binary mode and using passive mode.

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import java.io.FileInputStream;
import java.io.IOException;
public class FtpUploadExample {
    public static void uploadFile(FTPClient ftpClient, String localFilePath, String remoteFilePath) throws IOException {
        // Enter passive mode
        ftpClient.enterLocalPassiveMode();
        // Set file type to binary
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        // The remote file path might include directories. Make sure they exist.
        String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
        if (!ftpClient.changeWorkingDirectory(remoteDir)) {
            System.out.println("Could not change to remote directory: " + remoteDir);
            return;
        }
        try (InputStream inputStream = new FileInputStream(localFilePath)) {
            System.out.println("Uploading file: " + localFilePath);
            boolean done = ftpClient.storeFile(remoteFilePath, inputStream);
            if (done) {
                System.out.println("File uploaded successfully.");
            } else {
                System.out.println("File upload failed. Server reply: " + ftpClient.getReplyString());
            }
        }
    }
}

Example 3: Downloading a File (Download)

This is the reverse of uploading.

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class FtpDownloadExample {
    public static void downloadFile(FTPClient ftpClient, String remoteFilePath, String localFilePath) throws IOException {
        // Enter passive mode
        ftpClient.enterLocalPassiveMode();
        // Set file type to binary
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        try (OutputStream outputStream = new FileOutputStream(localFilePath)) {
            System.out.println("Downloading file: " + remoteFilePath);
            boolean success = ftpClient.retrieveFile(remoteFilePath, outputStream);
            if (success) {
                System.out.println("File downloaded successfully.");
            } else {
                System.out.println("File download failed. Server reply: " + ftpClient.getReplyString());
            }
        }
    }
}

Example 4: Listing Files in a Directory

This example lists the files and directories in the current or a specified remote directory.

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import java.io.IOException;
public class FtpListExample {
    public static void listFiles(FTPClient ftpClient, String path) throws IOException {
        // Enter passive mode
        ftpClient.enterLocalPassiveMode();
        // Set file type to ASCII is fine for listing, but binary is a safe default
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        FTPFile[] files = ftpClient.listFiles(path);
        System.out.println("Listing files for: " + (path.isEmpty() ? "current directory" : path));
        if (files != null && files.length > 0) {
            for (FTPFile file : files) {
                String details = file.getName();
                if (file.isDirectory()) {
                    details += " (DIR)";
                } else if (file.isFile()) {
                    details += " (" + file.getSize() + " bytes)";
                } else {
                    details += " (UNKNOWN)";
                }
                System.out.println(details);
            }
        } else {
            System.out.println("No files found in directory.");
        }
    }
}

Advanced: Handling FTPS (FTP over SSL/TLS)

For secure file transfers, you should use FTPS. The process is very similar but requires enabling security.

There are two security levels:

  • Implicit FTPS: The connection is secured from the very beginning on port 990. This is older and less common.
  • Explicit FTPS (Recommended): You start with a regular FTP connection and then explicitly request to upgrade the connection to use SSL/TLS. This is more flexible and widely supported.

Here’s how to do Explicit FTPS:

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import javax.net.ssl.SSLContext;
import java.io.IOException;
public class FtpsExample {
    public static void main(String[] args) {
        String server = "ftp.example.com";
        int port = 21; // Standard FTP port, will upgrade to secure
        String user = "username";
        String pass = "password";
        // Use FTPSClient instead of FTPClient
        FTPSClient ftpsClient = new FTPSClient("TLS"); // Use "SSL" for older servers
        try {
            // 1. Connect
            System.out.println("Connecting to " + server);
            ftpsClient.connect(server, port);
            int replyCode = ftpsClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                System.out.println("FTP server refused connection.");
                return;
            }
            // 2. Login
            System.out.println("Logging in as " + user);
            if (!ftpsClient.login(user, pass)) {
                System.out.println("Login failed.");
                return;
            }
            // 3. Enter passive mode and set file type
            ftpsClient.enterLocalPassiveMode();
            ftpsClient.setFileType(FTP.BINARY_FILE_TYPE);
            // 4. Explicitly upgrade the connection to secure
            // This is the key step for Explicit FTPS
            ftpsClient.execPBSZ(0); // Set protection buffer size
            ftpsClient.execPROT("P"); // Set data channel to private (encrypted)
            System.out.println("Connection secured and login successful.");
            // ... now you can perform secure upload/download/list operations ...
            // Example: Upload a file securely
            // uploadFile(ftpsClient, "local.txt", "remote.txt");
        } catch (IOException ex) {
            System.out.println("Error: " + ex.getMessage());
            ex.printStackTrace();
        } finally {
            try {
                if (ftpsClient.isConnected()) {
                    ftpsClient.logout();
                    ftpsClient.disconnect();
                    System.out.println("Disconnected from the server.");
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Best Practices and Troubleshooting

  • Always use Passive Mode: ftpClient.enterLocalPassiveMode(); is your friend.
  • Always use Binary Mode: ftpClient.setFileType(FTP.BINARY_FILE_TYPE); prevents file corruption for non-text files.
  • Check Return Codes: Never assume an operation succeeded. Check the return code or use a boolean return value. Use ftpClient.getReplyString() for a human-readable error message from the server.
  • Use try-with-resources: For InputStream and OutputStream to ensure they are always closed, even if an exception occurs.
  • Handle IOException: Network operations are unreliable. Wrap your code in try-catch blocks.
  • Common Problems:
    • Connection timed out / No route to host: Check the server address and port. Ensure the server is running and accessible from your network.
    • 530 User cannot log in: Wrong username or password.
    • 425 Cannot open data connection: Almost always means you are not in passive mode and the server cannot connect back to your client due to a firewall/NAT. Enable passive mode.
    • File is corrupted after transfer: You are likely in ASCII mode for a binary file. Set the file type to BINARY.
分享:
扫描分享到社交APP
上一篇
下一篇