sftp实现OS之间的远程传输文件与文件夹

本文介绍使用 JSch 库实现 Linux 和 Windows 环境间通过 SFTP 进行文件传输的方法。提供了连接 SFTP 服务器、上传、下载、删除文件及文件夹等功能的 Java 实现。

问题场景

最近的项目可能会要求从Linux环境与Windows进行文件传输,实现方式很多,本文采用com.jcraft.jsch_0.1.31.jar,支持递归上传、下载、删除文件夹或者文件。算是对scp实现OS之间的远程传输文件的补充。

CODE

package com.business.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;

/**
 * @author  youxingyang
 * @Date    2017-6-26 上午9:27:32
 */
public final class FtpUtils {

    private static String separator = "/";
     /**
     * 连接sftp服务器
     * @param host     主机
     * @param port     端口
     * @param username 用户名
     * @param password 密码
     * @return
     */
    public static ChannelSftp connect(String host, int port, String username, String password) {

        ChannelSftp sftp = null;
        try {
            JSch jsch = new JSch();
            jsch.getSession(username, host, port);
            Session sshSession = jsch.getSession(username, host, port);
            System.out.println("Session created.");
            sshSession.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            System.out.println("Session connected.");
            System.out.println("Opening Channel.");
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            sftp = (ChannelSftp) channel;
            System.out.println("Connected to " + host + ".");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sftp;
    }

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

    /**
     * 删除-单个文件
     * @param directory  要删除文件所在目录
     * @param deleteFile 要删除的文件
     * @param sftp
     */

    public void delete(String directory, String deleteFile, ChannelSftp sftp) {
        try {
            sftp.cd(directory);
            sftp.rm(deleteFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除文件夹或者文件
     * @param path 文件夹或者文件路径
     * @param sftp sftp
     * @param resursion 是否递归删除文件夹
     * @throws Exception 异常
     */
    public static void deleteFile(String path, ChannelSftp sftp, boolean recursion) throws Exception {
        try {
            if (isDirExist(sftp, path)) {
                if (recursion)
                    doDeleteFile(path, sftp);
                else
                    sftp.rmdir(path);
            } else {
                 sftp.rm(path);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 递归删除执行
     * @param pathString 文件路径
     * @param sftp sftp连接
     * @throws SftpException
     */
    private static void doDeleteFile(final String pathString, final ChannelSftp sftp) throws SftpException {
        @SuppressWarnings("unchecked")
        Vector<LsEntry> vector = sftp.ls(pathString);
        if (vector.size() == 1) { // 文件,直接删除
            sftp.rm(pathString);
        } else if (vector.size() == 2) { // 空文件夹,直接删除
            sftp.rmdir(pathString);
        } else {
            String fileName = "";
            // 删除文件夹下所有文件
            for (LsEntry en : vector) {
                fileName = en.getFilename();
                if (".".equals(fileName) || "..".equals(fileName)) {
                    continue;
                } else {
                    doDeleteFile(pathString + separator + fileName, sftp);
                }
            }
            // 删除文件夹
            sftp.rmdir(pathString);//rmdir只能删除空的文件夹
        }
    }

    public static void mkdir(String directory, ChannelSftp sftp) {
        try {
            sftp.ls(directory);
        } catch (SftpException e) {
            try {
                sftp.mkdir(directory);
            } catch (SftpException ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void mkdirs(String directory, ChannelSftp sftp) {
        List<String> fileNames = getFileNames(directory);
        StringBuffer buf = new StringBuffer();
        if (fileNames != null && fileNames.size() > 0) {
            for (String fileName : fileNames) {
                buf.append(separator);
                buf.append(fileName);
                mkdir(buf.toString(), sftp);
            }
        }
    }

    /**
     * 列出目录下的所有文件及文件夹
     * @param dir
     * @return
     */
    public static List<String> getFileNames(String dir) {
        File file = new File(dir);
        List<String> list = new ArrayList<String>();
        if (file.getParentFile() != null) {
            List<String> fileNames = getFileNames(file.getParentFile().getPath());
            for (String fileName : fileNames) {
                if (fileName != null && !fileName.equals(""))
                    list.add(fileName);
            }
        }
        if (!file.getName().equals(""))
            list.add(file.getName());
        return list;
    }

    /**
     * 下载文件-单个文件
     * @param directory    下载目录
     * @param downloadFile 下载的文件
     * @param saveFile     存在本地的路径
     * @param sftp
     */

    public void download(String directory, String downloadFile, String saveFile, ChannelSftp sftp) {
        try {
            sftp.cd(directory);
            File file = new File(saveFile);
            sftp.get(downloadFile, new FileOutputStream(file));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 下载文件-支持文件夹
     * @param sftp          sftp
     * @param ftpPath       ftp上的路径-文件夹或者文件路径
     * @param localPath     本地路径
     */
    public static void downloadFile(ChannelSftp sftp, String ftpPath, String localPath) {
        try {
            //要下载的是一个目录
            if (isDirExist(sftp, ftpPath)) {
                Vector<?> v = sftp.ls(ftpPath);
                for (Object object : v) {
                    ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) object;
                    doDownloadFile(sftp, entry, ftpPath, localPath);
                }
            } else {
                File file = new File(ftpPath);
                sftp.get(localPath, new FileOutputStream(file));
            }
        } catch (SftpException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从远端递归下载数据到本地-支持文件夹
     * @param sftp
     * @param entry         文件实体
     * @param ftpPath       ftp上的文件路径
     * @param localPath     存储下载文件的路径
     */
    public static void doDownloadFile(ChannelSftp sftp, ChannelSftp.LsEntry entry, String ftpPath, String localPath) {
        try {
            SftpATTRS attrs = entry.getAttrs();
            String filename = entry.getFilename();
            if (filename.equals(".") || filename.equals("..")) {
                return;
            }
            String nextFtpPath = getRealName(ftpPath, filename);
            String nextLocalPath = getRealName(localPath, filename);
            if (attrs.isDir()) {
                sftp.cd(nextFtpPath);

                Vector<?> v = sftp.ls(sftp.pwd());
                for (Object object : v) {
                    ChannelSftp.LsEntry entry1 = (ChannelSftp.LsEntry) object;
                    doDownloadFile(sftp, entry1, nextFtpPath, nextLocalPath);
                }
            } else {
                File file = new File(nextLocalPath);
                // 如果路径不存在,则创建
                File parentFile = file.getParentFile();
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }
                sftp.get(nextFtpPath, new FileOutputStream(nextLocalPath));
            }
        } catch (SftpException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static String getRealName(String pwd, String filename) {
        return pwd.endsWith(separator) ? pwd + filename : pwd + separator + filename;
    }

    /**
     * 上传文件-单个文件
     *
     * @param directory  上传的目录
     * @param uploadFile 要上传的文件
     * @param sftp
     */
    public void upload(InputStream is, String directory, String uploadFile, ChannelSftp sftp) {
        try {
            sftp.cd(directory);
            File file = new File(uploadFile);
            sftp.put(is, file.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 上传文件-支持文件夹
     * @param sftp          sftp
     * @param localPath     要传的文件夹或者文件路径
     * @param ftpPath       目标位置
     */
    public static void uploadFile(ChannelSftp sftp, String localPath, String ftpPath) {

        try {
            //如果目标目录不存在则捕获异常创建
            try {
                sftp.cd(ftpPath);
            } catch (SftpException e) {
                mkdirs(ftpPath, sftp);
                sftp.cd(ftpPath);
            }
            File file = new File(localPath);
            doUploadFile(sftp, file, sftp.pwd());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从本地递归上传数据到远端-支持文件夹
     * @param sftp  sftp
     * @param file  文件或者文件夹
     * @param pwd   当前目录
     */
    public static void doUploadFile(ChannelSftp sftp, File file, String pwd) {
        try {
            if (file.isDirectory()) {
                File[] list = file.listFiles();
                String fileName = file.getName();
                sftp.cd(pwd);
                pwd = pwd + separator + file.getName();
                if (!isDirExist(sftp, pwd)) {
                    sftp.mkdir(fileName);
                }
                //切换到下一级文件夹
                sftp.cd(file.getName());
                for (File aList : list) {
                    doUploadFile(sftp, aList, pwd);
                }
            } else {
                sftp.cd(pwd);
                sftp.put(new FileInputStream(file), file.getName());
            }
        } catch (SftpException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

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

    public static void main(String[] args) {
        String host = "192.168.XXX.XXX";
        int port = 22;
        String username = "root";
        String password = "master";
        ChannelSftp sftp = connect(host, port, username, password);

        // upload paths
        String ftpPathUpload = "/samples/a/b/";//不是root权限注意能否使用根目录
        //file
        //String localPathFileUpload = "/Users/yyx/IdeaProjects/study/lib/com.jcraft.jsch_0.1.31.jar";
        //directory
        String localPathDirUpload = "E:\\1报告数据\\XXX\\全自动化\\全自动化-x\\XXX\\TB_0001";

        //download paths
        String localPathDownload = "E:\\TB_0001";
        String ftpPathDownload = "/samples/a/b/TB_0001/";
        try {

            uploadFile(sftp, localPathDirUpload, ftpPathUpload);
            System.out.println("upload finished");

            downloadFile(sftp, ftpPathDownload, localPathDownload);
            System.out.println("download finished");

            deleteFile("/samples/a/b/", sftp, true);
            System.out.println("delete finished");

        } catch (Exception e) {
            System.out.println(e.getMessage());  
        } finally {
            sftp.quit();
            sftp.getSession().disconnect();
        }
    }
}
### 使用 PyQt 实现 FTP 远程文件传输功能 以下是关于如何通过 PyQt 和 Python 的 `ftplib` 库实现 FTP 文件上传下载的详细说明: #### 功能概述 为了实现 FTP 文件传输,可以利用 PyQt 提供的 GUI 工具包创建界面,并结合 `ftplib` 完成实际的数据传输逻辑。以下是一个完整的示例代码,展示如何构建一个简单的应用程序来完成 FTP 文件的上传和下载。 --- #### 示例代码 ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QFileDialog, QLabel import ftplib class FTPClient(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle('FTP Client') # 创建布局 layout = QVBoxLayout() # 添加标签用于显示状态 self.status_label = QLabel("Ready", self) layout.addWidget(self.status_label) # 选择本地文件夹按钮 btn_select_folder = QPushButton('Select Folder', self) btn_select_folder.clicked.connect(self.select_folder) layout.addWidget(btn_select_folder) # 上传按钮 btn_upload = QPushButton('Upload to FTP', self) btn_upload.clicked.connect(lambda: self.upload_files('/path/to/remote/folder')) layout.addWidget(btn_upload) # 下载按钮 btn_download = QPushButton('Download from FTP', self) btn_download.clicked.connect(lambda: self.download_files('/path/to/local/folder')) layout.addWidget(btn_download) self.setLayout(layout) def select_folder(self): folder_path = QFileDialog.getExistingDirectory(self, 'Select Directory') if folder_path: self.folder_path = folder_path self.status_label.setText(f'Selected folder: {folder_path}') def upload_files(self, remote_dir): try: ftp = ftplib.FTP("your_ftp_server_host") # 替换为你的FTP主机地址[^2] ftp.login(user="your_username", passwd="your_password") # 登录凭证 local_dir = getattr(self, 'folder_path', None) if not local_dir: self.status_label.setText("No folder selected!") return for root, dirs, files in os.walk(local_dir): relative_root = os.path.relpath(root, local_dir) remote_subdir = os.path.join(remote_dir, relative_root).replace("\\", "/") try: ftp.mkd(remote_subdir) # 如果目录不存在,则创建它 except Exception as e: pass # 忽略已存在的错误 for file_name in files: with open(os.path.join(root, file_name), "rb") as f: ftp.storbinary(f"STOR {os.path.join(remote_subdir, file_name)}", f) # 上传文件[^4] ftp.quit() # 关闭连接[^3] self.status_label.setText("Files uploaded successfully.") except Exception as e: self.status_label.setText(f"Error uploading files: {str(e)}") def download_files(self, local_dir): try: ftp = ftplib.FTP("your_ftp_server_host") # 替换为你的FTP主机地址 ftp.login(user="your_username", passwd="your_password") # 登录凭证 remote_dir = '/path/to/remote/folder' # 替换为目标远程目录 ftp.cwd(remote_dir) for entry in ftp.mlsd(): name, details = entry if details['type'] == 'file': with open(os.path.join(local_dir, name), "wb") as f: ftp.retrbinary(f'RETR {name}', f.write) # 下载文件 ftp.quit() # 关闭连接 self.status_label.setText("Files downloaded successfully.") except Exception as e: self.status_label.setText(f"Error downloading files: {str(e)}") if __name__ == '__main__': app = QApplication(sys.argv) client = FTPClient() client.show() sys.exit(app.exec_()) ``` --- #### 代码解析 1. **GUI 构建**: - 使用 PyQt5 中的组件(如 `QPushButton`, `QLabel` 等)设计了一个简单易用的图形化界面。 - 用户可以通过界面上的按钮选择本地文件夹、触发上传或下载操作。 2. **FTP 操作**: - 利用了标准库 `ftplib` 来处理 FTP 协议的具体细节,包括登录、切换工作目录以及文件的上传和下载。 - 对于上传操作,使用了递归遍历本地文件夹的方式,逐层复制整个结构至目标服务器;而对于下载操作,则逐一获取远程文件并保存到指定位置。 3. **异常处理**: - 针对可能出现的各种网络问题或者权限不足等情况进行了基本捕获,在发生错误时能够及时反馈给用户。 4. **资源释放**: - 不论成功否都会调用 `quit()` 方法确保最终断开服务器之间的链接。 --- #### 注意事项 - 在部署前需修改脚本内的敏感参数(例如服务器地址、用户名密码等),使其适配具体的环境需求。 - 考虑安全性因素建议采用更先进的协议比如 SFTP 或 HTTPS 加密通道替代传统的明文传送方式。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值