项目中,需要使用ftp服务器上传下载文件,之前做过sftp的文件上传下载,以为是一个东西,迅速的把之前的工具类拿过来使用,发现文件为空,特此记录一下二者的区别和工具类。

SFTP(Secure File Transfer Protocol)和FTP(File Transfer Protocol)是两种用于文件传输的协议,它们之间有几个主要区别:

  1. 安全性:SFTP是基于SSH(Secure Shell)的协议,通过加密传输数据,提供更高的安全性。而FTP在传输过程中不加密数据,安全性较低。

  2. 端口:SFTP使用SSH协议的默认端口22进行通信,而FTP使用默认端口21。由于SFTP使用SSH协议,它可以通过SSH隧道进行访问,绕过防火墙限制。

  3. 支持的功能:SFTP支持文件和目录的列表、上传、下载、删除等基本操作,还支持文件和目录的权限设置。而FTP支持更多的功能,如文件重命名、目录创建和删除、文件夹递归传输等。

  4. 连接方式:SFTP建立在SSH连接之上,需要使用SSH服务器的用户名和密码进行身份验证。而FTP可以使用用户名和密码进行身份验证,也可以使用匿名访问。

总的来说,SFTP提供了更高的安全性和更简单的配置,适用于需要保护数据传输的场景。而FTP则更加灵活,支持更多的功能,适用于需要更多文件传输选项的场景。

相关工具类:

pom.xml中分别引入

<!-- sftp -->
<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

<!-- ftp -->
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>

sftpUtils:

@Slf4j
public class SftpUtils {


    private ChannelSftp sftp;

    private Session session;
    /**
     * FTP 登录用户名
     */
    private String username;
    /**
     * FTP 登录密码
     */
    private String password;
    /**
     * 私钥
     */
    private String privateKey;
    /**
     * FTP 服务器地址IP地址
     */
    private String host;
    /**
     * FTP 端口
     */
    private int port;


    /**
     * 构造基于密码认证的sftp对象
     *
     * @param username
     * @param password
     * @param host
     * @param port
     */
    public SftpUtils(String username, String password, String host, int port) {
        this.username = username;
        this.password = password;
        this.host = host;
        this.port = port;
    }

    /**
     * 构造基于秘钥认证的sftp对象
     *
     * @param username
     * @param host
     * @param port
     * @param privateKey
     */
    public SftpUtils(String username, String host, int port, String privateKey) {
        this.username = username;
        this.host = host;
        this.port = port;
        this.privateKey = privateKey;
    }

    public SftpUtils() {
    }

    /**
     * 连接sftp服务器
     *
     * @throws Exception
     */
    public void login() {
        try {
            JSch jsch = new JSch();
            if (privateKey != null) {
                // 设置私钥
                jsch.addIdentity(privateKey);
                log.info("sftp connect,path of private key file:{}", privateKey);
            }
            log.debug("sftp connect by host:{} username:{}", host, username);

            session = jsch.getSession(username, host, port);
            log.info("Session is build");
            if (password != null) {
                session.setPassword(password);
            }
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");

            session.setConfig(config);
            session.connect();
            log.info("Session is connected");

            Channel channel = session.openChannel("sftp");
            channel.connect();
            log.info("channel is connected");

            sftp = (ChannelSftp) channel;
            log.info(String.format("sftp server host:[%s] port:[%s] is connect successfull", host, port));
        } catch (JSchException e) {
            log.error("Cannot connect to specified sftp server : {}:{}  Exception message is: {}", host, port, e.getMessage());
        }
    }

    /**
     * 关闭连接 server
     */
    public void logout() {
        if (sftp != null) {
            if (sftp.isConnected()) {
                sftp.disconnect();
                log.info("sftp is closed already");
            }
        }
        if (session != null) {
            if (session.isConnected()) {
                session.disconnect();
                log.info("sshSession is closed already");
            }
        }
    }

    /**
     * 将输入流的数据上传到sftp作为文件
     *
     * @param directory    上传到该目录
     * @param sftpFileName sftp端文件名
     * @param input        输入流
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String sftpFileName, InputStream input) throws SftpException {
        try {
            // 目录不存在则创建目录
            createDir(directory);
            sftp.cd(directory);
            sftp.put(input, sftpFileName);
        } catch (SftpException e) {
            log.warn("directory is not exist");
            sftp.mkdir(directory);
            sftp.cd(directory);
            sftp.put(input, sftpFileName);
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        log.info("file:{} is upload successful", sftpFileName);
    }


    /**
     * 创建一个文件目录
     */
    public void createDir(String createpath) {
        try {
            if (isDirExist(createpath)) {
                return;
            }
            String[] pathArry = createpath.split("/");
            StringBuilder filePath = new StringBuilder("/");
            for (String path : pathArry) {
                if (path.equals("")) {
                    continue;
                }
                filePath.append(path).append("/");
                if (isDirExist(filePath.toString())) {
                } else {
                    // 建立目录
                    sftp.mkdir(filePath.toString());

                }
            }

        } catch (SftpException e) {
            log.error("创建文件失败");
        }
    }


    /**
     * 判断目录是否存在
     */
    public boolean isDirExist(String directory) {
        boolean isDirExistFlag = false;
        try {
            SftpATTRS sftpATTRS = sftp.lstat(directory);
            isDirExistFlag = true;
            return sftpATTRS.isDir();
        } catch (Exception e) {
            if (e.getMessage().toLowerCase().equals("no such file")) {
                isDirExistFlag = false;
            }
        }
        return isDirExistFlag;
    }


    /**
     * 上传单个文件
     *
     * @param directory  上传到sftp目录
     * @param uploadFile 要上传的文件,包括路径
     * @throws FileNotFoundException
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String uploadFile) throws FileNotFoundException, SftpException {
        File file = new File(uploadFile);
        upload(directory, file.getName(), new FileInputStream(file));
    }

    /**
     * 将byte[]上传到sftp,作为文件。注意:从String生成byte[]是,要指定字符集。
     *
     * @param directory    上传到sftp目录
     * @param sftpFileName 文件在sftp端的命名
     * @param byteArr      要上传的字节数组
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String sftpFileName, byte[] byteArr) throws SftpException {
        upload(directory, sftpFileName, new ByteArrayInputStream(byteArr));
    }

    /**
     * 将字符串按照指定的字符编码上传到sftp
     *
     * @param directory    上传到sftp目录
     * @param sftpFileName 文件在sftp端的命名
     * @param dataStr      待上传的数据
     * @param charsetName  sftp上的文件,按该字符编码保存
     * @throws UnsupportedEncodingException
     * @throws SftpException
     * @throws Exception
     */
    public void upload(String directory, String sftpFileName, String dataStr, String charsetName) throws UnsupportedEncodingException, SftpException {
        upload(directory, sftpFileName, new ByteArrayInputStream(dataStr.getBytes(charsetName)));
    }

    /**
     * 下载文件
     *
     * @param directory    下载目录
     * @param downloadFile 下载的文件
     * @param outputStream  输出流
     * @throws SftpException
     * @throws FileNotFoundException
     * @throws Exception
     */
    public void download(String directory, String downloadFile, ByteArrayOutputStream outputStream) throws SftpException, FileNotFoundException {
        if (directory != null && !"".equals(directory)) {
            sftp.cd(directory);
        }
        sftp.get(downloadFile, outputStream);
        log.info("file:{} is download successful", downloadFile);
    }

    /**
     * 下载文件
     * @param directory    下载目录
     * @param downloadFile 下载的文件名
     * @return 字节数组
     * @throws SftpException
     * @throws IOException
     * @throws Exception
     */
    public byte[] download(String directory, String downloadFile) throws SftpException, IOException {
        if (directory != null && !"".equals(directory)) {
            sftp.cd(directory);
        }
        InputStream is = sftp.get(downloadFile);
        byte[] fileData = IOUtils.toByteArray(is);
        log.info("file:{} is download successful", downloadFile);
        return fileData;
    }

    /**
     * 删除文件
     *
     * @param directory  要删除文件所在目录
     * @param deleteFile 要删除的文件
     * @throws SftpException
     * @throws Exception
     */
    public void delete(String directory, String deleteFile) throws SftpException {
        sftp.cd(directory);
        sftp.rm(deleteFile);
    }

    /**
     * 列出目录下的文件
     *
     * @param directory 要列出的目录
     * @return
     * @throws SftpException
     */
    public Vector<?> listFiles(String directory) throws SftpException {
        return sftp.ls(directory);
    }
}

FTP:


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
/**
 * FTP工具类
 */
public class FtpUtil {
    private static Logger log = Logger.getLogger(FtpUtil.class);
    private FTPClient ftp;
    public FtpUtil() {
        ftp = new FTPClient();
        ftp.setControlEncoding("UTF-8"); //解决上传文件时文件名乱码
    }
    public FtpUtil(String controlEncoding) {
        ftp = new FTPClient();
        ftp.setControlEncoding(controlEncoding); //解决上传文件时文件名乱码
    }
    public void setTimeOut(int defaultTimeoutSecond, int connectTimeoutSecond, int dataTimeoutSecond){
        try {
            ftp.setDefaultTimeout(defaultTimeoutSecond * 1000);
                   ftp.setSoTimeout(connectTimeoutSecond * 1000); 
            ftp.setDataTimeout(dataTimeoutSecond * 1000);
        } catch (SocketException e) {
            log.error("setTimeout Exception:", e);
        }
    }
    public FTPClient getFTPClient(){
        return ftp;
    }
    public void setControlEncoding(String charset){
        ftp.setControlEncoding(charset);
    }
    public void setFileType(int fileType) throws IOException {
        ftp.setFileType(fileType);
    }
    /**
     * Connect to FTP server.
     * 
     * @param host
     *            FTP server address or name
     * @param port
     *            FTP server port
     * @param user
     *            user name
     * @param password
     *            user password
     * @throws IOException
     *             on I/O errors
     */
    public FTPClient connect(String host, int port, String user, String password) throws IOException {
        // Connect to server.
        try {
            ftp.connect(host, port);
        } catch (UnknownHostException ex) {
            throw new IOException("Can't find FTP server '" + host + "'");
        }
        // Check rsponse after connection attempt.
        int reply = ftp.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            disconnect();
            throw new IOException("Can't connect to server '" + host + "'");
        }
        if ("".equals(user)) {
            user = "anonymous";
        }
        // Login.
        if (!ftp.login(user, password)) {
            disconnect();
            throw new IOException("Can't login to server '" + host + "'");
        }
        // Set data transfer mode.
        ftp.setFileType(FTP.BINARY_FILE_TYPE);
        //ftp.setFileType(FTP.ASCII_FILE_TYPE);
        // Use passive mode to pass firewalls.
        ftp.enterLocalPassiveMode();
        return ftp;
    }
    /**
     * Test connection to ftp server
     * 
     * @return true, if connected
     */
    public boolean isConnected() {
        return ftp.isConnected();
    }
    /**
     * Disconnect from the FTP server
     * 
     * @throws IOException
     *             on I/O errors
     */
    public void disconnect() throws IOException {
        if (ftp.isConnected()) {
            try {
                ftp.logout();
                ftp.disconnect();
            } catch (IOException ex) {
            }
        }
    }
    /**
     * Get file from ftp server into given output stream
     * 
     * @param ftpFileName
     *            file name on ftp server
     * @param out
     *            OutputStream
     * @throws IOException
     */
    public void retrieveFile(String ftpFileName, OutputStream out) throws IOException {
        try {
            // Get file info.
            FTPFile[] fileInfoArray = ftp.listFiles(ftpFileName);
            if (fileInfoArray == null || fileInfoArray.length == 0) {
                throw new FileNotFoundException("File '" + ftpFileName + "' was not found on FTP server.");
            }
            // Check file size.
            FTPFile fileInfo = fileInfoArray[0];
            long size = fileInfo.getSize();
            if (size > Integer.MAX_VALUE) {
                throw new IOException("File '" + ftpFileName + "' is too large.");
            }
            // Download file.
            if (!ftp.retrieveFile(ftpFileName, out)) {
                throw new IOException("Error loading file '" + ftpFileName + "' from FTP server. Check FTP permissions and path.");
            }
            out.flush();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ex) {
                }
            }
        }
    }
    /**
     * Put file on ftp server from given input stream
     * 
     * @param ftpFileName
     *            file name on ftp server
     * @param in
     *            InputStream
     * @throws IOException
     */
    public void storeFile(String ftpFileName, InputStream in) throws IOException {
        try {
            if (!ftp.storeFile(ftpFileName, in)) {
                throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
            }
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
            }
        }
    }
    /**
     * 修改名称
     * @param from
     * @param to
     * @throws IOException
     */
    public boolean rename(String from, String to) throws IOException {
        return ftp.rename(from, to);
    }
    /**
     * Delete the file from the FTP server.
     * 
     * @param ftpFileName
     *            server file name (with absolute path)
     * @throws IOException
     *             on I/O errors
     */
    public void deleteFile(String ftpFileName) throws IOException {
        if (!ftp.deleteFile(ftpFileName)) {
            throw new IOException("Can't remove file '" + ftpFileName + "' from FTP server.");
        }
    }
    /**
     * Upload the file to the FTP server.
     * 
     * @param ftpFileName
     *            server file name (with absolute path)
     * @param localFile
     *            local file to upload
     * @throws IOException
     *             on I/O errors
     */
    public void upload(String ftpFileName, File localFile) throws IOException {
        // File check.
        if (!localFile.exists()) {
            throw new IOException("Can't upload '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");
        }
        // Upload.
        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(localFile));
            if (!ftp.storeFile(ftpFileName, in)) {
                throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
            }
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
            }
        }
    }
    /**
     * 上传目录(会覆盖)
     * @param remotePath 远程目录 /home/test/a
     * @param localPath 本地目录 D:/test/a
     * @throws IOException
     */
    public void uploadDir(String remotePath, String localPath) throws IOException {
        File file = new File(localPath);
        if (file.exists()) {
            if(!ftp.changeWorkingDirectory(remotePath)){
                ftp.makeDirectory(remotePath);    //创建成功返回true,失败(已存在)返回false
                ftp.changeWorkingDirectory(remotePath);    //切换成返回true,失败(不存在)返回false
            }
            File[] files = file.listFiles();
            for (File f : files) {
                if (f.isDirectory() && !f.getName().equals(".") && !f.getName().equals("..")) {
                    uploadDir(remotePath + "/" + f.getName(), f.getPath());
                } else if (f.isFile()) {
                    upload(remotePath + "/" + f.getName(), f);
                }
            }
        }
    }
    /**
     * Download the file from the FTP server.
     * 
     * @param ftpFileName
     *            server file name (with absolute path)
     * @param localFile
     *            local file to download into
     * @throws IOException
     *             on I/O errors
     */
    public void download(String ftpFileName, File localFile) throws IOException {
        // Download.
        OutputStream out = null;
        try {
            // Get file info.
            FTPFile[] fileInfoArray = ftp.listFiles(ftpFileName);
            if (fileInfoArray == null || fileInfoArray.length == 0) {
                throw new FileNotFoundException("File " + ftpFileName + " was not found on FTP server.");
            }
            // Check file size.
            FTPFile fileInfo = fileInfoArray[0];
            long size = fileInfo.getSize();
            if (size > Integer.MAX_VALUE) {
                throw new IOException("File " + ftpFileName + " is too large.");
            }
            // Download file.
            out = new BufferedOutputStream(new FileOutputStream(localFile));
            if (!ftp.retrieveFile(ftpFileName, out)) {
                throw new IOException("Error loading file " + ftpFileName + " from FTP server. Check FTP permissions and path.");
            }
            out.flush();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ex) {
                }
            }
        }
    }
    /**
     * 下载目录(会覆盖)
     * @param remotePath 远程目录 /home/test/a
     * @param localPath 本地目录 D:/test/a
     * @return
     * @throws IOException
     */
    public void downloadDir(String remotePath, String localPath) throws IOException {
        File file = new File(localPath);
        if(!file.exists()){
            file.mkdirs();
        }
        FTPFile[] ftpFiles = ftp.listFiles(remotePath);
        for (int i = 0; ftpFiles!=null && i<ftpFiles.length; i++) {
            FTPFile ftpFile = ftpFiles[i];
            if (ftpFile.isDirectory() && !ftpFile.getName().equals(".") && !ftpFile.getName().equals("..")) {
                downloadDir(remotePath + "/" + ftpFile.getName(), localPath + "/" + ftpFile.getName());
            } else {
                download(remotePath + "/" + ftpFile.getName(), new File(localPath + "/" + ftpFile.getName()));
            }
        }
    }
    /**
     * List the file name in the given FTP directory.
     * 
     * @param filePath
     *            absolute path on the server
     * @return files relative names list
     * @throws IOException
     *             on I/O errors
     */
    public List<String> listFileNames(String filePath) throws IOException {
        List<String> fileList = new ArrayList<String>();
        FTPFile[] ftpFiles = ftp.listFiles(filePath);
        for (int i = 0; ftpFiles!=null && i<ftpFiles.length; i++) {
            FTPFile ftpFile = ftpFiles[i];
            if (ftpFile.isFile()) {
                fileList.add(ftpFile.getName());
            }
        }
        return fileList;
    }
    /**
     * List the files in the given FTP directory.
     * 
     * @param filePath
     *            directory
     * @return list
     * @throws IOException
     */
    public List<FTPFile> listFiles(String filePath) throws IOException {
        List<FTPFile> fileList = new ArrayList<FTPFile>();
        FTPFile[] ftpFiles = ftp.listFiles(filePath);
        for (int i = 0; ftpFiles!=null && i<ftpFiles.length; i++) {
            FTPFile ftpFile = ftpFiles[i];
//            FfpFileInfo fi = new FfpFileInfo();
//            fi.setName(ftpFile.getName());
//            fi.setSize(ftpFile.getSize());
//            fi.setTimestamp(ftpFile.getTimestamp());
//            fi.setType(ftpFile.isDirectory());
            fileList.add(ftpFile);
        }
        return fileList;
    }
    /**
     * Send an FTP Server site specific command
     * 
     * @param args
     *            site command arguments
     * @throws IOException
     *             on I/O errors
     */
    public void sendSiteCommand(String args) throws IOException {
        if (ftp.isConnected()) {
            try {
                ftp.sendSiteCommand(args);
            } catch (IOException ex) {
            }
        }
    }
    /**
     * Get current directory on ftp server
     * 
     * @return current directory
     */
    public String printWorkingDirectory() {
        if (!ftp.isConnected()) {
            return "";
        }
        try {
            return ftp.printWorkingDirectory();
        } catch (IOException e) {
        }
        return "";
    }
    /**
     * Set working directory on ftp server
     * 
     * @param dir
     *            new working directory
     * @return true, if working directory changed
     */
    public boolean changeWorkingDirectory(String dir) {
        if (!ftp.isConnected()) {
            return false;
        }
        try {
            return ftp.changeWorkingDirectory(dir);
        } catch (IOException e) {
        }
        return false;
    }
    /**
     * Change working directory on ftp server to parent directory
     * 
     * @return true, if working directory changed
     */
    public boolean changeToParentDirectory() {
        if (!ftp.isConnected()) {
            return false;
        }
        try {
            return ftp.changeToParentDirectory();
        } catch (IOException e) {
        }
        return false;
    }
    /**
     * Get parent directory name on ftp server
     * 
     * @return parent directory
     */
    public String printParentDirectory() {
        if (!ftp.isConnected()) {
            return "";
        }
        String w = printWorkingDirectory();
        changeToParentDirectory();
        String p = printWorkingDirectory();
        changeWorkingDirectory(w);
        return p;
    }
    /**
     * 创建目录
     * @param pathname
     * @throws IOException
     */
    public boolean makeDirectory(String pathname) throws IOException {
        return ftp.makeDirectory(pathname);
    }
    public static void main(String[] args) throws Exception {
        FtpUtil ftpUtil = new FtpUtil("UTF-8");
        ftpUtil.connect("1.2.3.4", 21, "testuser", "testuser");
        //ftpUtil.setTimeOut(60, 60, 60);
        ftpUtil.upload("/home/testuser/文件1.txt", new File("E:/image/FTPClient/FTPClient测试/文件1.txt"));
        ftpUtil.download("/home/testuser/文件1.txt", new File("E:/image/FTPClient/FTPClient测试/文件1.txt"));
        ftpUtil.uploadDir("/home/testuser/FTPClient测试", "E:/image/FTPClient/FTPClient测试");
        ftpUtil.downloadDir("/home/testuser/FTPClient测试", "E:/image/FTPClient/FTPClient测试");
        ByteArrayOutputStream bos = new ByteArrayOutputStream(); //自动增长
        ftpUtil.retrieveFile("/home/testuser/文件1.txt", bos);
        System.out.println(bos.size());
        String contentStr = new String(bos.toByteArray(),"GBK");
        System.out.println(contentStr);
        ftpUtil.disconnect();
    } 
}
09-21 23:44