springboot项目添加定时任务,用sftp推送zip包到目标服务器

用sftp推送zip包到目标服务器

  • 任务类里面,主要功能是,定时采集三张表的数据,并把数据转换成csv,三份csv压缩成一个加密的zip包,通过sftp推送到指定的目录下

配置类

@Data
@Configuration
@ConfigurationProperties(prefix = "sftp.zip")
public class SftpConfig {
    private String host;
    private int port = 22;
    private String username;
    private String password;
    private String remoteDir;
    private String zipPassword;
    private String cron;
}

任务类SftpTask

import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.sftp.SFTPException;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class SftpTask {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private SftpConfig sftpConfig;

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
    // 定义表格头
    private static final String V4_HEADER = "网段名称,是否是专业公司,专业公司名称,单位名称/具体业务信息,单位所属分类,单位性质,所属省份,所属地市,所属区县,使用方式,业务类型,使用状态,管理状态,机房,设备名称,Loopbak地址,接入端口信息";
    private static final String RA_HEADER = "地址范围,是否是专业公司,专业公司名称,单位名称/具体业务信息,单位所属分类,单位性质,所属省份,所属地市,所属区县,使用方式,业务类型,使用状态,管理状态,机房,设备名称,Loopbak地址,接入端口信息";


    @Scheduled(cron = "${sftp.zip.cron}")
    public void executeScheduledTask() {
        try {
            logger.info("开始执行SFTP ZIP上传任务: " + new Date());
// 定义压缩包的名字,例如TEST_20250415.zip
            String zipFileName = "TEST_" + DATE_FORMAT.format(new Date()) + ".zip";

            createEncryptedZip(zipFileName);
            uploadViaSftp(zipFileName);
            new File(zipFileName).delete();

            logger.info("SFTP ZIP上传任务完成: " + new Date());
        } catch (Exception e) {
            logger.error("SFTP ZIP上传任务失败:"+e.getMessage());
        }
    }

    private void createEncryptedZip(String zipFileName) throws Exception {
        ZipParameters parameters = new ZipParameters();
        parameters.setEncryptFiles(true);
        parameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD);

        String previousDayFormatted = getPreviousDayFormatted();

        ZipFile zipFile = new ZipFile(zipFileName, sftpConfig.getZipPassword().toCharArray());
        try {
            String v4FileName = "V4_Subnet_"+previousDayFormatted+".csv";
            // 1. IPV4地址规划数据CSV
            addCsvToZip(zipFile, parameters, v4FileName,
                    V4_HEADER, v4Mapper.getAllDataByDate(),
                    new CsvRowMapper<SftpDto>() {
                        @Override
                        public String mapRow(SftpDto info) {
                            return String.format(Locale.US, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
                                    // 如果字段可能为 null,则替换为空字符串 ""
                                    info.getSubnet() != null ? info.getSubnet() : "",
                                    "", // 直接传空字符串
                                    "", // 直接传空字符串
                                    info.getUnit_business() != null ? info.getUnit_business() : "",
                                    "", // 直接传空字符串
                                    "", // 直接传空字符串
                                    450000, // 固定值
                                    info.getIp_use_city() != null ? info.getIp_use_city() : "",
                                    "", // 直接传空字符串
                                    "", // 直接传空字符串
                                    "", // 直接传空字符串
                                    2, // 固定值
                                    5, // 固定值
                                    info.getRoom() != null ? info.getRoom() : "",
                                    info.getDevice_name() != null ? info.getDevice_name() : "",
                                    info.getLookback_address() != null ? info.getLookback_address() : "",
                                    "" // 直接传空字符串
                            );
                        }
                    });

//            // 2. IPV6地址规划数据CSV
            String v6FileName = "V6_Subnet_"+previousDayFormatted+".csv";
            addCsvToZip(zipFile, parameters, v6FileName,
                    V4_HEADER, v6Mapper.getAllDataByZero(),
                    new CsvRowMapper<SftpDto>() {
                        @Override
                        public String mapRow(SftpDto info) {
                            return String.format(Locale.US, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
                                    // 如果字段可能为 null,则替换为空字符串 ""
                                    info.getSubnet() != null ? info.getSubnet() : "",
                                    "", // 直接传空字符串
                                    "", // 直接传空字符串
                                    info.getUnit_business() != null ? info.getUnit_business() : "",
                                    "", // 直接传空字符串
                                    "", // 直接传空字符串
                                    450000, // 固定值
                                    info.getIp_use_city() != null ? info.getIp_use_city() : "",
                                    info.getIp_use_county() != null ? info.getIp_use_county() : "",
                                    "", // 直接传空字符串
                                    "", // 直接传空字符串
                                    2, // 固定值
                                    5, // 固定值
                                    info.getRoom() != null ? info.getRoom() : "",
                                    info.getDevice_name() != null ? info.getDevice_name() : "",
                                    info.getLookback_address() != null ? info.getLookback_address() : "",
                                    "" // 直接传空字符串
                            );
                        }
                    });
//
//            // 3. IP地址备案数据CSV
            String raFileName = "IP_address_"+previousDayFormatted+".csv";
            addCsvToZip(zipFile, parameters, raFileName,
                    RA_HEADER, raMapper.getAllDataByZero(),
                    new CsvRowMapper<SftpDto>() {
                        @Override
                        public String mapRow(SftpDto info) {
                            return String.format(Locale.US, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
                                    // 如果字段可能为 null,则替换为空字符串 ""
                                    info.getAddress() != null ? info.getAddress() : "",
                                    info.getIs_profession() != null ? info.getIs_profession() : "",
                                    info.getPro_co_name() != null ? info.getPro_co_name() : "",
                                    "",
                                    info.getUnit_type() != null ? info.getUnit_type() : "",
                                    info.getUnit_nature() != null ? info.getUnit_nature() : "",
                                    info.getIp_use_province() != null ? info.getIp_use_province() : "",
                                    info.getIp_use_city() != null ? info.getIp_use_city() : "",
                                    info.getIp_use_county() != null ? info.getIp_use_county() : "",
                                    info.getUse_type() != null ? info.getUse_type() : "",
                                    info.getIp_service_type() != null ? info.getIp_service_type() : "",
                                    3, // 固定值
                                    8, // 固定值
                                    info.getRoom() != null ? info.getRoom() : "",
                                    info.getDevice_name() != null ? info.getDevice_name() : "",
                                    info.getLookback_address() != null ? info.getLookback_address() : "",
                                    info.getInterface_data() !=null ? info.getInterface_data() : ""
                            );
                        }
                    });
        } finally {
            zipFile.close();
        }
    }

    private interface CsvRowMapper<T> {
        String mapRow(T item);
    }

    private <T> void addCsvToZip(ZipFile zipFile, ZipParameters parameters,
                                 String fileName, String header, List<T> dataList, CsvRowMapper<T> rowMapper) throws IOException {

        File tempFile = File.createTempFile(fileName.replace(".csv", ""), ".csv");
        try {
            // 写入CSV内容
            PrintWriter writer = null;
            try {
                writer = new PrintWriter(new OutputStreamWriter(
                        new FileOutputStream(tempFile), StandardCharsets.UTF_8));

                writer.println(header);
                for (T data : dataList) {
                    writer.println(rowMapper.mapRow(data));
                }
            } finally {
                if (writer != null) {
                    writer.close();
                }
            }
            parameters.setFileNameInZip(fileName);
            // 添加到ZIP
            zipFile.addFile(tempFile, parameters);
        } finally {
            FileUtils.deleteQuietly(tempFile);
        }
    }

    private void uploadViaSftp(String zipFileName) throws Exception {
        try (SSHClient ssh = new SSHClient()) {
            // 跳过主机密钥验证(仅用于测试)
            ssh.addHostKeyVerifier(new HostKeyVerifier() {
                @Override
                public boolean verify(String hostname, int port, PublicKey key) {
                    return true; // 跳过主机密钥验证(仅用于测试)
                }

                @Override
                public List<String> findExistingAlgorithms(String hostname, int port) {
                    return Collections.emptyList();
                }
            });
            ssh.connect(sftpConfig.getHost(), sftpConfig.getPort());

            // 认证
            ssh.authPassword(sftpConfig.getUsername(), sftpConfig.getPassword());


            // 打开 SFTP 客户端并上传文件
            try (SFTPClient sftp = ssh.newSFTPClient()) {
                // 确保远程目录存在(支持多层目录)
                ensureDirectoryExists(sftp, sftpConfig.getRemoteDir());

                CustomLocalSourceFile customFile = new CustomLocalSourceFile(new File(zipFileName));
                // 指定远程路径
                String remotePath = sftpConfig.getRemoteDir() + new File(zipFileName).getName(); // 目标路径
                sftp.put(customFile, remotePath); // 上传到指定的远程路径
            }

            logger.info("文件上传成功:{}",zipFileName);
        } catch (Exception e) {
            logger.error("文件上传失败:{}",e.getMessage());
        }
    }

    public static String getPreviousDayFormatted() {
        // 定义日期格式
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");

        // 获取当前时间
        Date now = new Date();

        // 格式化日期
        return dateFormat.format(now);
    }

    /**
     * 确保远程目录存在(支持多层目录)
     *
     * @param sftp      SFTP 客户端
     * @param remoteDir 远程目录路径
     * @throws IOException 如果操作失败
     */
    private void ensureDirectoryExists(SFTPClient sftp, String remoteDir) throws IOException {
        // 分割路径为多个部分
        String[] dirs = remoteDir.split("/");
        StringBuilder currentPath = new StringBuilder("/");

        for (String dir : dirs) {
            if (!dir.isEmpty()) {
                currentPath.append(dir).append("/");
                try {
                    // 检查当前路径是否存在
                    sftp.stat(currentPath.toString());
                } catch (SFTPException e) {
                    // 如果路径不存在,则创建目录
                    if (e.getMessage().contains("No such file")) {
                        sftp.mkdir(currentPath.toString());
                    } else {
                        // 如果是其他异常,重新抛出
                        throw e;
                    }
                }
            }
        }
    }
}

配置文件 application.properties

  • sftp.zip.cron : 执行频率
  • sftp.zip.host : 接收端服务器IP地址
  • sftp.zip.port : 协议端口,默认22
  • sftp.zip.username : 接收端服务器登录用户名
  • sftp.zip.password : 接收端服务器登录密码
  • sftp.zip.remote-dir : 接收端文件接收路径
  • sftp.zip.zip-password : 压缩包密码
sftp.zip.cron=0 03 * * * ?
sftp.zip.host=192.168.195.98
sftp.zip.port=22
sftp.zip.username=root
sftp.zip.password=123456
sftp.zip.remote-dir=/mya/test/
sftp.zip.zip-password=666

pom.xml

 <!-- Zip4j for encrypted ZIP -->
        <dependency>
            <groupId>net.lingala.zip4j</groupId>
            <artifactId>zip4j</artifactId>
            <version>2.11.5</version>
        </dependency>
        <!-- JSch for SFTP -->
        <dependency>
            <groupId>com.hierynomus</groupId>
            <artifactId>sshj</artifactId>
            <version>0.32.0</version>
        </dependency>

        <!-- Configuration Processor -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值