springboot从SFTP读取csv文件

需求描述:从SFTP读取csv文件写入本地数据库中

1. SFTP服务器配置

  • application.yml
sftp:
  host: 127.0.0.1
  passWord: 123456
  port: 22
  userName: jack

2. 代码实现

  • 依赖
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-csv</artifactId>
</dependency>
  • SftpClient.java
package com.gigrt.framework.client;

import cn.hutool.core.collection.CollUtil;
import com.gigrt.common.exception.FileSyncException;
import com.gigrt.framework.config.SftpProperties;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.io.*;
import java.util.*;

@Slf4j
@Component
public class SftpClient {
    private static final String SLASH = "/";

    private static final String DOT = ".";

    private static final String DOUBLE_DOT = "..";

    private ChannelSftp sftp = null;

    private Session sshSession = null;

    @Autowired
    SftpProperties sftpProperties;

    /**
     * 连接sftp服务器
     */
    private void connect() {
        log.info("ftp连接开始host=" + sftpProperties.getHost() + "port" + sftpProperties.getPort() + "username=" + sftpProperties.getUserName());
        JSch jsch = new JSch();
        try {
            jsch.getSession(sftpProperties.getUserName(), sftpProperties.getHost(), sftpProperties.getPort());
            sshSession = jsch.getSession(sftpProperties.getUserName(), sftpProperties.getHost(), sftpProperties.getPort());
            log.info("ftp---Session created.");
            sshSession.setPassword(sftpProperties.getPassWord());
            Properties properties = new Properties();
            properties.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(properties);
            sshSession.connect();
            log.info("ftp---Session connected.");
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            log.info("Opening Channel.");
            sftp = (ChannelSftp) channel;
            log.info("ftp---Connected to " + sftpProperties.getHost());
        } catch (JSchException e) {
            log.error("sftp connect异常", e);
            throw new FileSyncException("connect异常");
        }
    }


    private void disconnect() {
        if (this.sftp != null && this.sftp.isConnected()) {
            this.sftp.disconnect();
            this.sftp = null;
            log.info("sftp is closed already");
        }
        if (this.sshSession != null && this.sshSession.isConnected()) {
            this.sshSession.disconnect();
            this.sshSession = null;
            log.info("sshSession is closed already");
        }
    }


    /**
     * 下载单个文件
     */
    public byte[] downloadFile(String filePath) {
        log.info("sftp下载文件 filePath{} 开始", filePath);
        connect();
        try (
                InputStream inputStream = sftp.get(filePath);
                ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        ) {
            //进入FTP服务器文件目录
            log.info("DownloadFile:{},success from sftp", filePath);
            log.info("ftp下载文件结束");
            byte[] buff = new byte[1024];
            int rc = 0;
            while ((rc = inputStream.read(buff, 0, 1024)) > 0) {
                swapStream.write(buff, 0, rc);
            }

            // in_b为转换之后的结果
            byte[] in_b = swapStream.toByteArray();
            return in_b;
        } catch (SftpException | IOException e) {
            log.error("sftp下载文件失败");
            throw new FileSyncException("ftp下载文件失败");
        } finally {
            disconnect();
        }
    }


    public void uploadFile(byte[] data, String directory, String remoteFileName) {
        log.info("sftp 上传文件{} 开始", remoteFileName);
        connect();
        try (InputStream is = new ByteArrayInputStream(data)) {
            this.createDirs(directory, sftp);
            sftp.put(is, remoteFileName);
        } catch (SftpException | IOException e) {
            log.error("sftp upload error", e);
            throw new FileSyncException("sftp upload error");
        } finally {
            disconnect();
        }
    }


    /**
     * 列出文件
     *
     * @param directory 目录
     * @return 文件列表
     */
    public List<String> listFiles(String directory, String matchRule) {
        log.info("sftp listFiles 目录是:{} 开始,matchRule", directory);
        connect();
        try {
            Vector files = sftp.ls(directory);
            if (CollUtil.isNotEmpty(files)) {
                List<String> list = new ArrayList<>(files.size());
                for (Object object : files) {
                    ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) object;
                    log.info("fileName:{}", file.getFilename());
                    //文件名需包含后缀名以及排除   .和 ..
                    boolean match = file.getFilename().contains(matchRule);
                    if (!DOT.equals(file.getFilename()) && !DOUBLE_DOT.equals(file.getFilename())
                            && file.getFilename().contains(DOT) && match) {
                        String newFilePath = directory.endsWith(SLASH) ? directory + file.getFilename() : directory + SLASH + file.getFilename();
                        list.add(newFilePath);
                    }
                }
                return list;
            }
        } catch (SftpException e) {
            log.error("sftp list files error", e);
            throw new FileSyncException("sftp list files error");
        } finally {
            disconnect();
        }
        return Collections.emptyList();
    }

    /**
     * 列出文件
     *
     * @param directory 目录
     * @return 文件列表
     */
    public List<String> listFiles(String directory) {
        log.info("sftp listFiles 目录是:{} 开始,matchRule", directory);
        connect();
        try {
            Vector files = sftp.ls(directory);
            if (CollUtil.isNotEmpty(files)) {
                List<String> list = new ArrayList<>(files.size());
                for (Object object : files) {
                    ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) object;
                    log.info("fileName:{}", file.getFilename());
                    // 排除   .和 ..
                    if (!DOT.equals(file.getFilename()) && !DOUBLE_DOT.equals(file.getFilename())
                            && file.getFilename().contains(DOT)) {
                        String newFilePath = directory.endsWith(SLASH) ? directory + file.getFilename() : directory + SLASH + file.getFilename();
                        list.add(newFilePath);
                    }
                }
                return list;
            }
        } catch (SftpException e) {
            log.error("sftp list files error", e);
            throw new FileSyncException("sftp list files error");
        } finally {
            disconnect();
        }
        return Collections.emptyList();
    }

    /**
     * 创建SFTP目录,如果目录不存在就创建
     *
     * @param dirPath 源目录
     * @param sftp    sftp
     */
    private void createDirs(@NotNull String dirPath, ChannelSftp sftp) {
        String[] dirs = Arrays.stream(dirPath.split("/")).filter(StringUtils::isNotBlank).toArray(String[]::new);
        for (String dir : dirs) {
            try {
                sftp.cd(dir);
                log.info("Change directory {}", dir);
            } catch (Exception e) {
                try {
                    sftp.mkdir(dir);
                    log.info("Create directory {}", dir);
                } catch (SftpException e1) {
                    log.error("Create directory failure, directory:{}", dir, e1);
                    throw new FileSyncException("Create directory failure");
                }
                try {
                    sftp.cd(dir);
                    log.info("Change directory {}", dir);
                } catch (SftpException e1) {
                    log.error("Change directory failure, directory:{}", dir, e1);
                    throw new FileSyncException("Create directory failure");
                }
            }
        }
    }
}


  • CsvHelper.java
package com.gigrt.framework.helpers;

import com.gigrt.common.exception.FileSyncException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class CsvHelper {

    private CsvHelper() {}

    public static CsvHelper getHelper() {
        return new CsvHelper();
    }

    /**
     * 读取csv文件,并转换成List<Map<String, String>>
     *
     * @param data       数据文件
     * @param limit      限制行数 -1 表示不限制
     * @param headerLine headerLine
     * @return 转换map 对象
     */
    public List<Map<String, Object>> read(byte[] data, Integer limit, Integer headerLine) {
        // 读取文件参数设置,根据实际情况设置
        CSVFormat format =
                CSVFormat.Builder.create()
                        .setDelimiter(',')
                        .setRecordSeparator("\r\n")
                        .setIgnoreEmptyLines(true)
                        .setAllowDuplicateHeaderNames(true)
                        .build();
        try (CSVParser parser = format.parse(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data))))) {
            return extractDataWithHeader(parser, limit, headerLine);
        } catch (Exception e) {
            log.error("csv读取错误!", e);
            throw new FileSyncException("csv读取错误!");
        }
    }


    /**
     * 这里处理了一种常见情况,正常字段中有换行符,就是一条数据被分成两条,这里的处理是反手拼接起来
     */
    private List<Map<String, Object>> extractDataWithHeader(CSVParser parser, int limit, int headerLine) {
        final List<Map<String, Object>> result = new LinkedList<>();
        List<String> headers = new ArrayList<>();
        for (CSVRecord row : parser) {
            final long rowNum = row.getRecordNumber();
            if (rowNum == headerLine) {
                headers = row.stream().collect(Collectors.toList());
                continue;
            }
            result.add(setRowRecord(headers, row));
            // 限制行数
            if (limit >= 0 && rowNum > limit) {
                return result;
            }
        }
        return result;
    }


    private Map<String, Object> setRowRecord(List<String> headers, CSVRecord row) {
        Map<String, Object> rowRecord = new HashMap<>(headers.size());
        for (int i = 0; i < headers.size(); i++) {
            if (i < row.size()) {
                rowRecord.put(headers.get(i), row.get(i));
            }
        }
        return rowRecord;
    }
}
  • Test.java
    说明:fileStrategy只是对SftpClient的封装, 这里忽略
@Test
    public void test04() throws IOException {
        List<String> strings = fileStrategy.listFiles("/ETL/FILE/");
        System.out.println(strings);
        strings.forEach(file -> {
            try {
                byte[] bytes = fileStrategy.downloadFile(file);
                System.out.println(bytes);
                List<Map<String, Object>> result = CsvHelper.getHelper().read(bytes, -1, 1);
                System.out.println(result);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });

    }

3. 实现逻辑

1. 首先从ftp读取到文件:sftp.get(filePath)
2. 将读取的文件转换为byte[], 供系统需要时使用.
3. 封装工具类CsvHelper, 用于转换byte[]为可读类型, 例如:List<Map<String, Object>>
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中使用SFTP协议来读取CSV文件可以通过以下步骤实现。 首先,我们需要添加所需的依赖项。在pom.xml文件中添加以下依赖项: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-sftp</artifactId> </dependency> <dependency> <groupId>net.schmizz</groupId> <artifactId>sshj</artifactId> <version>0.27.0</version> </dependency> ``` 接下来,我们需要设置SFTP连接的配置信息。可以在application.properties文件中添加以下属性: ``` spring.integration.sftp.host=your-sftp-host spring.integration.sftp.username=your-sftp-username spring.integration.sftp.password=your-sftp-password ``` 然后,我们可以编写一个服务类来读取SFTP上的CSV文件。可以使用`SftpInboundFileSynchronizer`来实现SFTP文件同步,使用`SftpInboundFileSynchronizingMessageSource`来生成SFTP文件输入端点。 在服务类中,我们可以定义一个方法来读取SFTP上的CSV文件: ```java @Service public class SftpService { @Value("${spring.integration.sftp.remote.directory}") private String sftpRemoteDirectory; @Bean public SftpInboundFileSynchronizer sftpInboundFileSynchronizer() { SftpInboundFileSynchronizer synchronizer = new SftpInboundFileSynchronizer(sessionFactory()); synchronizer.setDeleteRemoteFiles(false); synchronizer.setRemoteDirectory(sftpRemoteDirectory); return synchronizer; } @Bean @InboundChannelAdapter(channel = "sftpChannel", poller = @Poller(fixedDelay = "5000")) public MessageSource<File> sftpMessageSource() { SftpInboundFileSynchronizingMessageSource source = new SftpInboundFileSynchronizingMessageSource(sftpInboundFileSynchronizer()); source.setLocalDirectory(new File("local-directory")); source.setAutoCreateLocalDirectory(true); source.setLocalFilter(new AcceptOnceFileListFilter<File>()); return source; } @ServiceActivator(inputChannel = "sftpChannel") public void handleFile(Message<File> message) { File file = message.getPayload(); // 读取CSV文件的逻辑 } } ``` 在上述代码中,`sftpRemoteDirectory`属性为SFTP上的远程目录,`sftpInboundFileSynchronizer`方法用于创建SFTP文件同步器,`sftpMessageSource`方法用于创建SFTP文件输入端点。 `handleFile`方法用于处理读取到的CSV文件。 最后,我们可以在Spring Boot应用中使用这个服务类来读取SFTP上的CSV文件。可以通过调用`handleFile`方法来触发读取文件的操作。 以上就是使用Spring Boot读取SFTP上的CSV文件的简单实现。希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值