Springboot整合Jsch-Sftp

news2024/12/30 2:13:11

背景

  开发一个基于jsch的sftp工具类,方便在以后的项目中使用。写代码的过程记录下来,作为备忘录。。。

Maven依赖

  • springboot依赖
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.0</version>
</parent>
  • jsch依赖
 <dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>
  • 完整依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>jsch-sftp</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>

    <dependencies>

        <!-- web应用相关 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

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

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>

</project>

项目结构

在这里插入图片描述

Sftp连接工具类

  • JschSftpRun: 项目启动类
package cn.com.soulfox;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * 启动类
 *
 * @author xxxx
 * @create 2024/7/5 11:50
 */
@SpringBootApplication
public class JschSftpRun {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(JschSftpRun.class, args);
    }
}

  • JschSftpConneciton: 创建sftp连接工具类
      用于创建sftp连接,类中提供了创建sftp连接的方法,另外还提供了在创建sftp连接失败后,重新尝试创建连接的方法
package cn.com.soulfox.jsch;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;

/**
 * @author xxxx
 * @create 2024/7/4 12:15
 */
@Slf4j
public class JschSftpConneciton {

    private static final String SFTP_PROTOCOL = "sftp";
    private static final Integer DEFAULT_RETRY_COUNT_5 = 5;
    private static final Long SLEEP_ITME_1000 = 1000L;//每次重试的时间间隔

    private String host;
    private Integer port;
    private String username;
    private String password;
    private Integer sessionTimeout;
    private Integer connectionTimeout;

    public JschSftpConneciton setHost(String host) {
        this.host = host;
        return this;
    }

    public JschSftpConneciton setPort(Integer port) {
        this.port = port;
        return this;
    }

    public JschSftpConneciton setUsername(String username) {
        this.username = username;
        return this;
    }

    public JschSftpConneciton setPassword(String password) {
        this.password = password;
        return this;
    }

    public JschSftpConneciton setSessionTimeout(Integer sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
        return this;
    }

    public JschSftpConneciton setConnectionTimeout(Integer connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
        return this;
    }

    /**
     * 返回SftpWrapper对象,是为了方便释放Session,Channel,ChannelSftp资源
     * SFTP连接服务器
     */
    public SftpWrapper connect() throws  Exception {
        JSch jsch = new JSch();
        Session session = null;
        Channel channel = null;
        ChannelSftp sftp = null;
        try {

            session = jsch.getSession(username, host, port);
            if (session == null) {
                throw new JSchException("create session error");
            }
            //设置登陆主机的密码
            session.setPassword(password);
            //设置第一次登陆的时候提示,可选值:(ask | yes | no)
            session.setConfig("StrictHostKeyChecking", "no");
            //设置登陆超时时间
            session.connect(sessionTimeout);
            session.sendKeepAliveMsg();
            //创建sftp通信通道
            channel = session.openChannel(SFTP_PROTOCOL);
            channel.connect(connectionTimeout);
            sftp = (ChannelSftp) channel;

            return new SftpWrapper(session, channel, sftp);
        }catch (JSchException e){
            log.error("SFTP连接异常:", e);
            if (sftp != null ) {
                sftp.disconnect();
            }
            if (channel != null ) {
                channel.disconnect();
            }
            if (session != null ) {
                session.disconnect();
            }
            throw e;
        } catch (Exception e1) {
            log.error("SFTP连接异常:", e1);
            if (sftp != null ) {
                sftp.disconnect();
            }
            if (channel != null ) {
                channel.disconnect();
            }
            if (session != null ) {
                session.disconnect();
            }
            throw e1;
        }
    }

    /**
     * 获取sftp连接,获取连接失败时会重试,
     * 默认重试次数:5次
     * @return
     * @throws Exception
     */
    public SftpWrapper connectWithRetry() throws Exception {
        return connectWithRetry(DEFAULT_RETRY_COUNT_5);
    }
    public SftpWrapper connectWithRetry(int retryCount) throws Exception {
        //最大重试次数
        int maxRetryCount = DEFAULT_RETRY_COUNT_5;
        if(retryCount > 0){
            maxRetryCount = retryCount;
        }

        int retry = 0;
        Throwable t = null;
        do {
            try {
                SftpWrapper channelSftpWrapper = this.connect();
                if(channelSftpWrapper != null){
                    t = null;
                    return channelSftpWrapper;
                }
            } catch (Exception e) {
                t = e;
            }

            try {
                Thread.sleep(SLEEP_ITME_1000);//休息1秒
            } catch (InterruptedException e) {
            }
        }while (retry++ < maxRetryCount);
        if(t != null){
            throw new Exception(t);
        }
        return null;
    }

}

  • SftpWrapper: sftp连接对象包装类,属性包括Session,Channel,ChannelSftp
      执行sftp操作有ChannelSftp就可以了,使用Sftp包装类,是为了方便关闭资源
package cn.com.soulfox.jsch;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;
import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * SftpWrapper类简单包装ChannelSftl对象,方便关闭资源
 * @author xxxx
 * @create 2024/7/4 14:26
 */
@AllArgsConstructor//生成全字段,构造方法
@Data //生成 getter,setter方法
public class SftpWrapper {
    private Session session = null;
    private Channel channel = null;
    private ChannelSftp sftp = null;

    /**
     * 关闭资源
     */
    public void disconnect() {
        if (sftp != null && sftp.isConnected()) {
            sftp.disconnect();
        }
        if (channel != null && channel.isConnected()) {
            channel.disconnect();
        }
        if (session != null && session.isConnected()) {
            session.disconnect();
        }
    }
}

  • JschSftpConfig: jsch sftp配置类
      初始化 jsch sftp
package cn.com.soulfox.config;

import cn.com.soulfox.jsch.JschSftpConneciton;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

/**
 * @author xxxx
 * @create 2024/7/4 12:12
 */
@Configuration
public class JschSftpConfig {

    @Value("${jsch.sftp.host}")
    private String host;
    @Value("${jsch.sftp.port:22}")
    private Integer port;
    @Value("${jsch.sftp.username}")
    private String username;
    @Value("${jsch.sftp.password}")
    private String password;

    @Value("${jsch.sftp.session-timeout:60000}")
    private Integer sessionTimeout;//单位毫秒
    @Value("${jsch.sftp.connect-timeout:5000}")
    private Integer connectTimeout;//单位毫秒
    
    @Bean
    @Lazy
    public JschSftpConneciton jschSftpConneciton(){
        return new JschSftpConneciton()
        		.setHost(host)
                .setPort(port)
                .setUsername(username)
                .setPassword(password)
                .setSessionTimeout(sessionTimeout)
                .setConnectionTimeout(connectTimeout);
    }

}

sftp连接信息配置在文件application.yml里
jsch:
sftp:
host: 172.168.xxx.xx
port: 1221 #默认22,1221公司自己定义的,这里要配置正确
username: xxxx #远程服务器用户名
password: xxxx #远程服务器密码
sftp连接信息

SftpUitl 工具类,提供下载,上传文件功能

package cn.com.soulfox.jsch;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;

/**
 * @author xxxx
 * @create 2024/7/4 14:28
 */
@Component
@Slf4j
public class JschSftpUtil {

    private static JschSftpConneciton jschSftpConneciton;

    @Autowired
    public void setJschSftpConneciton(JschSftpConneciton jschSftpConneciton) {
        JschSftpUtil.jschSftpConneciton = jschSftpConneciton;
    }

    /**
     * 下载文件
     * @param remotePath            远程目录
     * @param downloadFileName      待下载的远程文件名称
     * @param localSavePath         下载文件保存的本地目录
     */
    public static void downloadFile(String remotePath, String downloadFileName, String localSavePath) {

        SftpWrapper sftpWrapper = null;

        try {
            //sftp连接对象
            sftpWrapper = jschSftpConneciton.connectWithRetry();

            //进入远程服务器指定的目录
            sftpWrapper.getSftp().cd(remotePath);

            if (!checkLocalPath(localSavePath)) {
                log.info("本地目录[{}]不存在,且新建失败+++++", localSavePath);
                return;
            }

            String localFile = checkPathEnd(localSavePath) + downloadFileName;
            File outFile = new File(localFile);

            sftpWrapper.getSftp().get(downloadFileName, new FileOutputStream(outFile));
            log.info("从远程目录[{}]下载文件[{}]到本地目录[{}]成功", remotePath, downloadFileName, localSavePath);

        } catch (SftpException e) {
            log.info("从远程目录[{}]下载文件[{}]到本地目录[{}]失败", remotePath, downloadFileName, localSavePath);
            log.error("下载文件失败: ", e);
        } catch (Exception e) {
            log.info("从远程目录[{}]下载文件[{}]到本地目录[{}]失败", remotePath, downloadFileName, localSavePath);
            log.error("下载文件失败: ", e);
        } finally {
            if (sftpWrapper != null) {
                sftpWrapper.disconnect();
            }
        }


    }

    /**
     *
     * @param localDir              保存上传文件的本地目录
     * @param uploadFileName        上传文件名称
     * @param remoteSaveDir         保存上传文件的远程目录, 建议使用绝对路径
     *                              如果使用相对路径,建议基准目录使用sfpt登录后所在的目录
     *                              这个目录,使用channelSftp.goHome()可以获取
     */
    public static void uploadFile(String localDir, String uploadFileName, String remoteSaveDir) {

        String uploadFilePath = checkPathEnd(localDir) + uploadFileName;
        File uploadFile = new File(uploadFilePath);
        uploadFile(uploadFile, remoteSaveDir);

    }

    /**
     * 上传文件
     * @param uploadFilePath        本地文件的路径
     * @param remoteSaveDir         保存上传文件的远程目录, 建议使用绝对路径
     *                              如果使用相对路径,建议基准目录使用sfpt登录后所在的目录
     *                              这个目录,使用channelSftp.goHome()可以获取
     */
    public static void uploadFile(String uploadFilePath, String remoteSaveDir) {
        File uploadFile = new File(uploadFilePath);
        uploadFile(uploadFile, remoteSaveDir);
    }

    /**
     * 上传文件
     * @param uploadFile        上传文件的File对象
     * @param remoteSavePath    保存上传文件的远程目录, 建议使用绝对路径
     *                          如果使用相对路径,建议基准目录使用sfpt登录后所在的目录
     *                          这个目录,使用channelSftp.goHome()可以获取
     */
    public static void uploadFile(File uploadFile, String remoteSavePath) {
        if(uploadFile == null ){
            log.info("本地文件对象不存在++++");
            return;
        }
        if(!uploadFile.exists()){
            log.info("本地文件[{}]不存在", uploadFile.getAbsoluteFile());
            return;
        }
        InputStream is = null;
        try {
            is = new FileInputStream(uploadFile);
        } catch (FileNotFoundException e) {
            log.info("获取本地文件[{}]的文件流失败", uploadFile.getAbsoluteFile());
            log.error("获取文件流失败: ", e);

            if(is != null){
                try {
                    is.close();
                } catch (IOException ioException) {
                }
            }
            return;
        }

        SftpWrapper sftpWrapper = null;
        try {
            //sftp连接对象
            sftpWrapper = jschSftpConneciton.connectWithRetry();

            //检查远程目录,不存在则创建
            createLinuxRemoteDirs(sftpWrapper.getSftp(), remoteSavePath);

            //进入用户home目录,
            sftpWrapper.getSftp().cd(sftpWrapper.getSftp().getHome());
            //保证当前目录在上传文件保存的目录
            sftpWrapper.getSftp().cd(remoteSavePath);

            //上传文件
            sftpWrapper.getSftp().put(is, uploadFile.getName());

        } catch (SftpException e) {
            log.info("上传本地文件[{}]到远程目录[{}]失败", uploadFile.getAbsoluteFile(), remoteSavePath);
            log.error("上传本地文件失败: ", e);
        } catch (Exception e) {
            log.info("上传本地文件[{}]到远程目录[{}]失败", uploadFile.getAbsoluteFile(), remoteSavePath);
            log.error("上传本地文件失败: ", e);
        } finally {
            if (sftpWrapper != null) {
                sftpWrapper.disconnect();
            }
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                }
            }
        }
    }

    /**
     * 检查目录结是否以目录分隔符结尾
     * 如果不是,则加上目录分隔符结尾
     *
     * @param localPath 本地目录
     * @return
     */
    private static String checkPathEnd(String localPath) {

        if (localPath.endsWith("/") || localPath.endsWith("\\")) {
            return localPath;
        }

        return localPath + File.separator;
    }

    /**
     * 检查本地目录是否存在,不存在就创建。
     * 为了防止其他线程已创建目录,加上了重试代码
     *
     * @param localPath 本地目录
     * @return
     */
    private static boolean checkLocalPath(String localPath) {

        int maxRetryCount = 5;
        int retry = 0;

        do {
            File localDir = new File(localPath);
            if (localDir.exists()) {
                return true;
            }
            boolean result = localDir.mkdirs();
            if (result) {
                return true;
            }

            try {
                Thread.sleep(1000L);//休息1秒
            } catch (InterruptedException e) {
            }
        } while (retry++ < maxRetryCount);

        return false;
    }

    /**
     * 检查和创建[ linux系统 ]的远程多级目录,
     * 外部调用, 单纯的创建远程目录,不作其他操作
     * @param remoteDir     远程目录
     * @throws SftpException
     */
    public static void createLinuxRemoteDirs( String remoteDir) throws SftpException {
        SftpWrapper sftpWrapper = null;
        try {
            sftpWrapper = jschSftpConneciton.connectWithRetry();
            createLinuxRemoteDirs(sftpWrapper.getSftp(), remoteDir);

        } catch (SftpException e) {
            log.info("创建Linux远程目录[{}]失败", remoteDir);
            log.error("创建Linux远程目录失败: ", e);
        } catch (Exception e) {
            log.info("创建Linux远程目录[{}]失败", remoteDir);
            log.error("创建Linux远程目录失败: ", e);
        } finally {
            if (sftpWrapper != null) {
                sftpWrapper.disconnect();
            }
        }
    }
    /**
     * 检查和创建[ linux系统 ]的远程多级目录,
     * linux系统目录分隔符是 “/”
     * 内部上传文件的方法调用
     *
     * @param sftpChannel
     * @param remoteDir  远程目录
     * @throws SftpException
     */
    public static void createLinuxRemoteDirs(ChannelSftp sftpChannel, String remoteDir) throws SftpException {

        if(remoteDir == null || "".equals(remoteDir)){
            log.info("待创建的远程目录为空++++++++");
            return;
        }
        String[] dirs = remoteDir.split("/");;

        if(dirs == null || dirs.length == 0){
            log.info("拆分目录[{}]失败,没有获取到目录数组", remoteDir);
            return;
        }

        //进入用户home目录,保证初始目录正确
        sftpChannel.cd(sftpChannel.getHome());

        if( dirs.length == 1){
            //只有一层目录,直接处理
            try {
                sftpChannel.cd(dirs[0]);
            } catch (SftpException e) {
                if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                    log.info("开始创建远程目录[{}]", dirs[0]);
                    sftpChannel.mkdir(dirs[0]);
                } else {
                    throw e;
                }
            }
            return;
        }

        StringBuilder sb = new StringBuilder();

        //处理第一个元素
        if(remoteDir.startsWith(".")){
            //相对路径,把缺少的路径补上
            sb.append(sftpChannel.getHome()).append("/");
        }else if(remoteDir.startsWith("/")){
            //绝对路径,把"/"放到目录开头
            sb.append("/");
        }else {
            //既不是”/“开头,也不是”.“开头
            //属于相对路径的一种情况
            try {
                //先处理第一层目录
                sftpChannel.cd(dirs[0]);
            } catch (SftpException e) {
                if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                    log.info("开始创建远程目录[{}]", dirs[0]);
                    sftpChannel.mkdir(dirs[0]);
                } else {
                    throw e;
                }
            }
            //把已处理的目录加上
            sb.append(sftpChannel.getHome()).append("/").append(dirs[0]).append("/");
        }


        //从第二个元素开始创建不存在的目录
        for (int i = 1; i < dirs.length; i++) {
            String dir = dirs[i];
            if (dir.isEmpty() ) {
                //跳过空字符串
                continue;
            }
            sb.append(dir + "/");
            try {
                sftpChannel.cd(sb.toString());
            } catch (SftpException e) {
                if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                    log.info("开始创建远程目录[{}]", sb.toString());
                    sftpChannel.mkdir(sb.toString());
                } else {
                    throw e;
                }
            }
        }
//        for (String dir : dirs) {
//            if (dir.isEmpty() || dir.contains(".")) {
//                //跳过空字符串,和"."字符串
//                continue;
//            }
//            sb.append(dir + "/");
//            try {
//                sftpChannel.cd(sb.toString());
//            } catch (SftpException e) {
//                if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
//                    sftpChannel.mkdir(sb.toString());
//                } else {
//                    throw e;
//                }
//            }
//        }
    }
}

单元测试

  • JschSftpConfigTest: 测试类
package cn.com.soulfox.config;

import cn.com.soulfox.JschSftpRun;
import cn.com.soulfox.jsch.JschSftpConneciton;
import cn.com.soulfox.jsch.JschSftpUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author xxxx
 * @create 2024/7/5 12:57
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JschSftpRun.class,
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class JschSftpConfigTest {


    @Autowired
    private JschSftpConneciton sftpConneciton;

    /**
     * 下载文件
     */
    @Test
    public void testDownload(){
        //从远程服务器上的下载 /home/jiyh/ 目录下的 testabc.txt
        //下载文件保存到 d:\jiyh
        String remotePath = "/home/jiyh/";
        String downloadFileName = "testabc.txt";
        String localSavePath = "d:\\jiyh";
        JschSftpUtil.downloadFile(remotePath, downloadFileName, localSavePath);
    }

    /**
     * 上传文件
     */
    @Test
    public void testUpload(){
        //上传传本地 d:\jiyh 目录下的 test123.txt文件
        //到远程 /home/jiyh/test/test 目录
        //目录不存在,会自动创建
        JschSftpUtil.uploadFile("d:\\jiyh", "test123.txt", "/home/jiyh/test/test");

    }
}

  • 在远程服务器准备测试文件 文件内容随便,这里我准备的内容为“112dadfdefee”
    在这里插入图片描述

  • 测试下载功能
    测试结果:
    在这里插入图片描述

  • 测试上传功能 :在本地 d:/jiyh 目录准备测试文件“test123.txt”,内容为“dfdfdfdfdaerwrt”
    测试结果
    在这里插入图片描述

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

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

相关文章

python中的ProgressMeter类【自定义】

python中的ProgressMeter类【自定义】 显示进度条 # 1.定义类ProgressMeter class ProgressMeter(object):def __init__(self, num_batches, *meters):# 初始化头&#xff0c;如&#xff1a;[ 100/2500]&#xff08;占符号位&#xff09;self.batch_fmtstr self._get_batch_…

JVM原理(三):JVM对象回收判定机制与回收算法

如何判断一个对象是否存活(即是否还分配在堆中)&#xff0c;那看他是否还在用。 1. 引用计数算法 这是一种判断方式&#xff0c;相应的方法就是&#xff1a;如果一个对象被引用&#xff0c;那将被引用的对象中的一个计数器加一&#xff0c;引用失效就减一。在任何时刻引用计数…

数千万“四高”中老年患者,如何推动国产营养保健品创新

“三高”指高血压、高血糖&#xff08;糖尿病&#xff09;、高血脂&#xff0c;是中老年群体的常见病。 然而&#xff0c;除了前述三者&#xff0c;高尿酸血症在我国的患病率正逐年提高&#xff0c;已成为仅次于糖尿病的第二大代谢性疾病。痛风是高尿酸血症典型症状之一。 加上…

Lesson 48 Do you like ... ? Do you want ... ?

Lesson 48 Do you like … ? Do you want … ? 词汇 fresh a. 新鲜的【食物】 搭配&#xff1a;fresh water 淡水    fresh man 新生    fresh air 新鲜空气    fresh egg 新鲜鸡蛋 例句&#xff1a;我们喜欢新鲜的空气。    We like fresh egg. egg n. 蛋【通…

电容的常用用法

1.降压&#xff0c;其实就是用电容去分压&#xff0c;没什么好说的 2.滤波&#xff0c;高频信号下&#xff0c;电容的容抗非常小&#xff0c;所以说容易让高频信号通过电容&#xff0c;而低频信号下电容容抗大&#xff0c;从而能够滤出高频信号 3.延时&#xff0c;电容充放电需…

优思学院与你探索六西格玛:从统计术语到现代质量管理方法

六西格玛&#xff0c;这个源自统计学的术语&#xff0c;已经在质量管理领域掀起了一场革命。然而&#xff0c;很多初学者&#xff0c;包括参加优思学院六西格玛课程的学生&#xff0c;常常对其真正的意义感到困惑。本文将带领大家深入了解六西格玛&#xff0c;从其统计学起源到…

Node.js的下载、安装和配置

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

SpringBoot 启动流程四

SpringBoot启动流程四 前面这个创建对象是初始化SpringApplication对象 是加载了SpringBoot程序的所有相关配置 我们接下来要将这个run方法 run过程是一个运行 初始化容器 我们看我们的运行结果是得到一个ConfigurableApplicationContext对象 package com.bigdata1421.star…

ChatMoney:AI看病,私人医生不是梦想!

本文由 ChatMoney团队出品 在当今这个科技飞速发展的时代&#xff0c;人工智能技术正在以惊人的速度改变着我们的生活&#xff0c;人工智能已经深入到各个领域&#xff0c;医疗行业也不例外。 而今天我要和大家聊一聊利用ChatMoney全能知识库AI系统在求医问诊领域所发挥的巨大…

Elasticsearch实战教程:如何使用集群索引数据来进行统计多个数据?

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 Elasticsearch聚合查询是一种强大的工具&#xff0c;允许我们对索引中的数据进行复杂的统计分析和计算。本文将详细解释一…

【分布式系统五】监控平台Zabbix实际监控运用(命令+截图详细版)

目录 一.Zabbix 监控 Windows 1.安装zabbix 2.Web 页面添加主机&#xff0c;关联模板 二.Zabbix 监控 Java 应用 1.安装tomcat 2.服务端安装 zabbix-java-gateway 3.Web 页面添加主机&#xff0c;关联模板 三.Zabbix 监控 SNMP 1.服务端安装 snmp 监控程序 2.修改 sn…

小型全自动气象站的知识分享

TH-QC5小型全自动气象站具有便携式一体化结构设计&#xff0c;外形美观&#xff0c;安装方便简捷&#xff0c;可靠运行于各种恶劣的野外环境。它能够实时监测温度、湿度、风速、风向、雨量、气压、光照等多种气象参数&#xff0c;并且这些气象观测要素的配置方式可以根据项目的…

stm32定时器与pwm波

文章目录 4 TIM4.1 SysTick系统定时器4.2 TIM定时器中断与微秒级延时4.3 TIM使用PWM波4.3.1 PWM介绍4.3.2 无源蜂鸣器实现 4.4 TIM ,PWM常用函数 4 TIM 4.1 SysTick系统定时器 ​ Systick系统滴答&#xff0c;&#xff08;同时他有属于自己的中断&#xff0c;可以利用它来做看…

OceanMind海睿思成功举办“数据要素×人工智能” 研讨会,荣获上海数交所授牌

近日&#xff0c;由南京东南人工智能产业链供应链创新链党建联盟、上海数据交易所主办&#xff0c;中新赛克承办的“数据要素人工智能”行业应用研讨会在南京成功举办。 本次活动是东南人工智能产业链供应链创新链党建联盟2024年度“人工智能人才服务行动项目”第二期活动。会议…

环境检测聚四氟乙烯微波消解罐 特氟龙反应釜 适用于COD测定

COD消解罐是实验室中用于测定水样中化学需氧量&#xff08;Chemical Oxygen Demand&#xff0c;简称COD&#xff09;的一种专用设备。化学需氧量是衡量水体污染程度的一个重要参数&#xff0c;它表示在一定条件下&#xff0c;水样中的有机物质和部分无机物质被氧化的程度。以下…

linux——小细节(Makefile)(gdb)

一、makefile a.out:main.c func.cgcc main.c func.cclean:rm a.out a.out:main.c func.cgcc $^ -o $clean:rm a.out SRCmain.c func.c OBJa.out CCgcc FLAG -g -lpthread $(OBJ):$(SRC)$(CC) $(SRC) $(FLAG)clean:rm $(OBJ) 二、gdb

玩客云刷Armbian安装docker、alist、OpenWrt、Aria2等教程及所需文件(内容详细)

这篇教程主要是本人在这里面踩的坑比较多&#xff0c;网上的教程很多&#xff0c;感觉都不太详细&#xff0c;分享一下自己的经验。 注意本教程的图片都是后期补的&#xff0c;可能与原文内容不符&#xff0c;仅供参考&#xff0c;图片里面会出现小雅&#xff0c;memos笔记&am…

记录第一次写脚本

使用csh语言&#xff0c;Linux系统操作的 写和执行csh&#xff08;C Shell&#xff09;脚本不需要额外的软件&#xff0c;只需要一个支持csh的终端环境。 1.检查是否安装了C Shell 在终端terminal运行以下命令 which csh 如果返回路径&#xff0c;比如/bin/csh&#xff0c…

昇思25天学习打卡营第17天|GAN图像生成

模型简介 GAN模型的核心在于提出了通过对抗过程来估计生成模型这一全新框架。在这个框架中&#xff0c;将会同时训练两个模型——捕捉数据分布的生成模型G和估计样本是否来自训练数据的判别模型D 。 在训练过程中&#xff0c;生成器会不断尝试通过生成更好的假图像来骗过判别…

怎么在表格后添加文字行行

Ctrl Shift 回车 解决方案 在表格末尾添加一行&#xff08;表格行&#xff09;。 &#xff08;此时光标应该默认在这个新行中&#xff0c;如果没有&#xff0c;自己手动点一下&#xff09; 按 Ctrl Shift 回车 将此行与前面的表格拆分开&#xff0c;中间会出现一个空文本行…