服务器间进行文件传输-SFTPSCP一篇搞定

news2024/9/20 12:15:58

1.简单介绍一下

在一些特殊场景,两台服务器之间需要进行文件传输的时候,或许我们会想到FTP,这也是我们常见的实现方式,但是如果我们不能操作远程服务器,无法判断远程服务器是否安装了FTP呢,众所周知,FTP使用的前提时确定服务器上配置了FTP服务,并且正在运行FTP服务器软件,这是最大的前提,如果我们不知道,那么就不能贸然的使用该方式。

我的需求是:我只知道对方服务器的ip、端口、用户名、密码,文件地址。其他的我一概不知。

那么还有什么方式呢,SFTP?SCP?

SFTP和FTP不就少了一个S,应该也需要安装FTP吧?答案是:NO

SFTP:是一种基于SSH协议的网络协议,用于在网络上安全的传输协议,SFTP提供了一个安全的文件传输机制,允许用户在传输过程中加密数据,从而保护数据免受窃听和篡改。

注意:OpenSSH支持SFTP,也就是说只要服务器中有OpenSSH,SFTP就可以使用。

FTP:是一种用于在网络上进行文件传输的协议。它是基于文本的协议,允许用户上传、下载、删除和重命名文件。

注意:需要安装配置FTP服务到服务器。

SCP:是一种用于在服务器之间安全复制文件的网络协议。基于SSH协议,提供了一个加密的方法来传输文件,确保数据在传输过程中的安全性。SCP是一个命令行工具,通常用于远程服务器管理、文件备份和系统维护。

注意:我认为只要服务器可以执行linux命令,应该都可以实现。

2.实现方式

我的实现方式是基于Spring Boot创建完成的哈,大家如果出现了什么问题,得放到评论区,得重新看看具体是什么原因。

2.1 FTP方式

这个大家就参考网上详细的文章哈,主要我的需求不能使用这个方式😄,所以没有代码😜。

2.2 SFTP方式

第一个肯定就是导入依赖

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.53</version>
</dependency>        

然后,咱们就得写一个工具类了

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import java.io.File;

public class ScpFileTransferUtil {

    // 远程服务器的IP地址
    private static final String IP = "192.xxx.xxx.xx";
    // 远程服务器的SSH端口
    private static final int PORT = xxxx;
    // 远程服务器的用户名
    private static final String USER_NAME = "root";
    // 远程服务器的密码
    private static final String PASSWORD = "xxxxxx";

    /**
     * 从远程服务器下载文件到本地
     *
     * @param remotePath 远程文件路径
     * @param localPath  本地目标目录
     */
    public static void scpFromRemote(String remotePath, String localPath) {
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(USER_NAME, IP, PORT);
            session.setPassword(PASSWORD);

            // 设置配置项
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            // 连接会话
            System.out.println("Connecting to SSH server...");
            session.connect();

            // 打开 SFTP 通道
            Channel channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp sftpChannel = (ChannelSftp) channel;

            // 解析远程文件路径中的文件名
            String fileName = extractFileNameFromPath(remotePath);

            // 创建本地文件路径
            File localFile = new File(localPath, fileName);

            // 创建本地目标目录
            File directory = localFile.getParentFile();
            if (!directory.exists()) {
                boolean mkdirResult = directory.mkdirs();
                if (mkdirResult) {
                    System.out.println("创建文件夹成功");
                } else {
                    System.out.println("创建文件夹失败");
                    throw new RuntimeException("创建文件夹失败!");
                }
            }

            // 下载文件
            System.out.println("Downloading file from remote path: " + remotePath);
            sftpChannel.get(remotePath, localFile.getAbsolutePath());

            System.out.println("文件下载成功");

            // 关闭通道和会话
            channel.disconnect();
            session.disconnect();

        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("文件下载失败:" + e.getMessage());
        }
    }

    /**
     * 从路径中提取文件名
     *
     * @param path 完整路径
     * @return 文件名
     */
    private static String extractFileNameFromPath(String path) {
        int lastSeparatorIndex = path.lastIndexOf(File.separator);
        return path.substring(lastSeparatorIndex + 1);
    }

    public static void main(String[] args) {
        System.out.println("Main method started.");
        //获取开始时间
        long startTime = System.currentTimeMillis();
        String remotePath = "/data/ceshiDown/eac207eeeb2095cc560f94b6e7e33f49_4.mp4"; // 远程文件路径
        String localPath = "E:\\mnt\\data_process_net\\token"; // 本地目标目录

        scpFromRemote(remotePath, localPath);
        System.out.println("Main method finished.");
        //获取结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("程序运行时间:" + (endTime - startTime)/1000 + "s");
    }
}

这段代码应该不需要我讲解一下了吧,我相信兄弟们都可以看懂的,必要的注释我都加上啦。

搞定!

2.3 SCP方式

SCP的实现方式,有一些稍微的麻烦,需要写一个expect脚本,为什么使用脚本呢,说明一下,在我们使用scp命令在拷贝远程服务器的时候,我们中间需要输入密码。

但是我们在执行代码的时候,可以自动识别需要输入密码,然后自动化输入密码吗?答案是不能的。

所以这里介入expect脚本的作用就是识别哪里需要输入密码,然后自动化输入密码。

这里应该不需要安装任何的依赖的。

直接就是工具类:

import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
@Slf4j
public class ScpUtil {


    public static void scpFromRemoteUsingExpect(String expectScriptPath, String user, String host, int port, String remotePath, String localPath, String password) {
        Process process = null;
        try {
            // 确保目标目录存在
            File localDir = new File(localPath);
            if (!localDir.exists()) {
                boolean created = localDir.mkdirs(); // 创建多级目录
                if (!created) {
                    throw new IOException("无法创建目标目录: " + localPath);
                }
            }
            // 构建 expect 脚本的命令行参数
            String[] params = {user, host, Integer.toString(port), remotePath, localPath, password};
            String command = expectScriptPath + " " + String.join(" ", params);

            // 执行 expect 脚本
            process = Runtime.getRuntime().exec(command);

            // 读取命令的输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待命令执行完成
            int exitCode = process.waitFor();
            if (exitCode == 0) {
                log.info("文件下载成功");
            } else {
                log.info("文件下载失败,退出代码:{}", exitCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("文件下载失败:{}", e.getMessage());
        }finally {
            // 确保资源被释放
            if (process != null) {
                process.destroy();
            }
        }
    }



    public static void scpDirectoryFromRemoteUsingExpect(String expectScriptPath, String user, String host, int port, String remotePath, String localPath, String password) {
        Process process = null;
        try {
            // 确保目标目录存在
            File localDir = new File(localPath);
            if (!localDir.exists()) {
                boolean created = localDir.mkdirs(); // 创建多级目录
                if (!created) {
                    throw new IOException("无法创建目标目录: " + localPath);
                }
            }
            // 构建 expect 脚本的命令行参数,确保 remotePath 是一个目录
            String[] params = {user, host, Integer.toString(port), remotePath + "/", localPath, password};
            String command = expectScriptPath + " " + String.join(" ", params);

            // 执行 expect 脚本
            process = Runtime.getRuntime().exec(command);

            // 读取命令的输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待命令执行完成
            int exitCode = process.waitFor();
            if (exitCode == 0) {
                System.out.println("文件夹下载成功");
            } else {
                System.err.println("文件夹下载失败,退出代码:" + exitCode);
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.info("文件夹下载失败:{}", e.getMessage());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 重新设置中断状态
            e.printStackTrace();
            log.info("文件夹下载被中断:{}", e.getMessage());
        } finally {
            // 确保资源被释放
            if (process != null) {
                try {
                    process.getInputStream().close();
                    process.getOutputStream().close();
                    process.getErrorStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                process.destroy();
            }
        }
    }

    public static void main(String[] args) {
        log.info("Main method started.");
        // 获取开始时间
        long startTime = System.currentTimeMillis();
        String expectScriptPath = "/script/scp_download_directory.exp"; // expect 脚本的路径
        String remotePath = "/data/ceshiDown/";
        String localPath = "/data/ceshi2";
        ScpUtil.scpDirectoryFromRemoteUsingExpect(expectScriptPath, USER_NAME, IP, PORT, remotePath, localPath, PASSWORD);
        log.info("Main method finished.");
        // 获取结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("程序运行时间:" + (endTime - startTime) / 1000 + "s");
    }
}

上面这个工具类呢,scpFromRemoteUsingExpect()方法是复制文件的,scpDirectoryFromRemoteUsingExpect()是复制文件夹的。注意一下哦。

最终要的来啦,脚本怎么写呢。
复制文件的脚本下载地址:scp复制远程服务器文件至本地服务器expect脚本

复制文件夹的脚本下载地址:scp复制远程服务器文件夹至本地服务器expect脚本

若是文件下载不了,千万别花钱下载,联系我,我发你。

这里我就不测试了,我是在服务器测试的,是行得通的。
完结!撒花!以上内容,若有任何问题,欢迎在评论区留言,在此,若有更好的解决方案的兄弟,也请留言,让我学习一下哈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2095519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习周报-2024.8.31

目录 摘要 Abstract 创新点总结 模型数学原理 实验设置 一、验证实验 二、对比实验 摘要 这周重新梳理出论文的三个创新点&#xff0c;对所提出方法进行数学原理验证&#xff0c;证明其可行性。重新设置了实验部分&#xff0c;分为验证实验和对比实验&#xff0c;一共四…

使用 Eigen 库中的 Kronecker 积运算

前言 在数值计算和线性代数的众多应用中&#xff0c;Kronecker 积&#xff08;Kronecker Product&#xff09;是一种常用的矩阵运算。Eigen 是一个高性能的 C 数值计算库&#xff0c;广泛用于科学计算和工程应用中。在 Eigen 库中&#xff0c;Kronecker 积运算属于不常用的扩展…

资料分析(1)

1)截三个数去做&#xff0c;属于马上进位了&#xff0c;差距小&#xff0c;1/19<10% 2)截两位数去做&#xff0c;1/18>10% 3)次位差分别是:3&#xff0c;4&#xff0c;1&#xff0c;选项差距分别是大&#xff0c;大&#xff0c;小 截尾不需要考虑数量级 算一半&#xff0…

抽奖项目技术亮点

活动是通过秒杀领取的。&#xff08;即&#xff1a;活动对应着某一商品&#xff09; 这里超卖指&#xff1a;对于一个活动它的参与量有数量限制&#xff0c;就是活动的库存&#xff0c;当活动的领取数大于活动库存总量&#xff0c;就是超卖 用户秒杀参与活动的资格&#xff08;…

一.海量数据实时分析-Doris入门和安装

前言 停了一个月又开始写文章啦&#xff0c;因为公司数据量达到了几十亿&#xff0c;老板需要做实时数据分析&#xff0c;报表看板。这么大的数据量比较好的选择是使用Doris来做&#xff0c;他可以脱离hadoop生态独立使用所以大受企业喜爱&#xff0c;也因为如此就有了这个系列…

【JavaWeb】Http请求与响应

文章目录 Http 请求与响应一、Http 请求格式1、请求行2、请求头3、请求体&#xff08;post请求才有&#xff09; 二、HttpServletRequest1、获取 请求行 信息2、获取 请求头 信息3、获取 请求参数 信息 三、Http 响应格式1、响应行2、响应头3、响应体&#xff08;正文&#xff…

AI大模型应用开发环境配置

目录 一、工具下载 1、Python官网下载 2、Pycharm官网下载 3、Streamlit官网下载 二、升级PIP &#xff08;一&#xff09;检查PIP版本 &#xff08;二&#xff09;在anaconda Prompt命令窗口输入 三、安装openai组件 四、安装streamlit组件 五、启动streamlit 一、工…

Voi滑板车公司助农扶商,着手打造流量板块

Voi滑板车公司助农扶商&#xff0c;着手打造流量板块。 吉林是粮食大省&#xff0c;是全国优质粳稻主产区&#xff0c;现阶段全省水稻年产量600多万吨&#xff08;商品量400万吨左右&#xff09;&#xff0c;占东北三省一区的24%。巍巍长白山、悠悠松江水&#xff0c;辽阔黑土…

Qt:玩转QPainter序列九(文本,文本框,填充)

前言 继续承接序列八 正文 1. drawImage系列函数 绘制图像 inline void drawImage(const QPoint &p, const QImage &image); 作用: 在指定的点 p 上绘制 QImage 图像。图像的左上角将对齐到 p 点。 inline void drawImage(int x, int y, const QImage &image,…

若依 Vue3的前后端分离系统管理 创建

RuoYi 若依官方网站 |后台管理系统|权限管理系统|快速开发框架|企业管理系统|开源框架|微服务框架|前后端分离框架|开源后台系统|RuoYi|RuoYi-Vue|RuoYi-Cloud|RuoYi框架|RuoYi开源|RuoYi视频|若依视频|RuoYi开发文档|若依开发文档|Java开源框架|Java|SpringBoot|SrpingBoot2.0…

【JPCS独立出版】第四届电气工程与计算机技术国际学术会议(ICEECT 2024,9月27-29)

第四届电气工程与计算机技术国际学术会议&#xff08;ICEECT2024&#xff09;将于9月27日-29日在哈尔滨举办。 会议主要围绕"电路与系统"、“电气工程材料”、“计算机视觉”、“计算机技术”等专业研究领域展开讨论。旨在为气工程、计算机技术等领域的专家学者及企业…

Java EE

Java EE 包含JavaSE 增加一些新的API 构建一个后端服务 网页->web服务器->java后端 web后端(javaEE)程序需要运行在服务器中的&#xff0c;这样前端才可以访问得到 服务器&#xff1a;是容器&#xff0c;是连接用户和程序之间的中间件 解释1&#xff1a;一款软件&#…

HBase 部署及shell操作

HBase 数据库 一、HBase 概述1.1 HBase 是什么HBase 的特点 二、HBase 模型及架构2.1 HBase 逻辑模型2.2 HBase 数据模型2.3 HBase 物理模型2.3.1 列簇物理模型2.3.2 Rowkey 字段排序2.3.3 Region 存储到不同节点2.3.4 Region 结构 2.4 HBase 基本架构 三、搭建 HBase 分布式集…

【Linux】线程结束

目录 线程安全和重入 死锁 STL中的容器不是线程安全的 线程安全的单例模式 自旋锁 读者写者问题 线程安全和重入 线程安全&#xff1a;多个线程并发执行同一段代码时&#xff0c;不会出现不同的&#xff08;异常的&#xff09;结果&#xff0c;我们就说线程是安全的。常见…

如何学好文件操作,快来看这篇文章(沉淀中)!!!!

文章目录 1. 为什么使用文件&#xff1f;2. 什么是文件&#xff1f;2.1 程序文件2.2 数据文件2.3 文件名 3. ⼆进制文件和文本文件&#xff1f;4. 文件的打开和关闭4.1 流和标准流4.1.1 流4.1.2 标准流 4.2 文件指针4.3 文件的打开和关闭 5. ⽂件的顺序读写5.1 顺序读写函数介绍…

jQuery库

注明&#xff1a;本文参考自&#xff1a;jQuery - 白月黑羽 (byhy.net) jQuery安装 Download jQuery | jQuery下载到本地 ps: script标签中的src属性&#xff1a;表示包含要执行的代码的外部文件位置 <!DOCTYPE html> <html lang"en"><head><s…

unity游戏开放:标记物体 一目了然

Unity游戏开发:标记物体,让开发变得一目了然 “好读书&#xff0c;不求甚解&#xff1b;每有会意&#xff0c;便欣然忘食。” 本文目录&#xff1a; Unity游戏开发 Unity游戏开发:标记物体,让开发变得一目了然前言1. 什么是Tag&#xff1f;2. Unity中如何添加和管理Tag步骤1&am…

vue如何引入element-ui

2.x用element-ui 3.x用element-plus https://blog.csdn.net/weixin_41207479/article/details/127066333 引入element-ui的三种方式

点餐API接口对接的过程中需要注意哪些问题

以下是点餐 API 接口对接的一般步骤&#xff1a; 选择合适的点餐 API 服务提供商&#xff1a;市面上有不少提供点餐 API 的平台。你需要根据自身业务需求、预算、接口的稳定性和性能、技术支持等因素来综合考量选择。注册与申请&#xff1a;在选定 API 服务提供商后&#xff0…

数据响应式

响应式原理 课堂主题 1.利用defineProperty实现数据劫持2.利用ES6中proxy实现数据劫持3.实现数据驱动视图更新&#xff0c;实现数据响应4.发布订阅模式 知识点 defineProperty&#xff1b;Proxy代理数据劫持发布订阅观察者模式与发布订阅数据响应式 defineProperty Objec…