欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Spring boot 实现查询列表上传至 FTP 服务器后生成 excel 列表(完整版)

最编程 2024-05-12 20:49:50
...
开源中国社区团队直播首秀,以分享为名讲述开源中国社区背后的故事”

app.yml配置:

ftp:
    #ip
    ip: 127.0.0.1
    #端口
    port: 21
    #用户名
    username: ftpadmin
    #密码
    password: xxxxxx
    #初始化连接数
    initialSize: 5
    #编码格式
    encoding: UTF-8
    #缓冲区
    bufferSize: 8192
    #是否开启
    isopen: true
    #当获取ftp客户端失败后的重试次数
    retryCount: 5
    path: /home/ftpadmin/

服务器安装FTP,且设置账号密码,以及创建一个目录:/home/ftpadmin/ 主要存放文件

Controller控制层 主要获取查询以及文件流
@Scheduled(cron = "15,30,45 * * * * ?")
    public void test() throws Exception {
        // 创建一个excel文件
        HSSFWorkbook book = new HSSFWorkbook();
        // 创建Sheet对象
        HSSFSheet sheet = book.createSheet("机构客户信息表");
        // 创建excel文件标题
        String[] tableHeader = {"创建人", "不稽查批次号", "角色ID", "角色List"};
        HSSFRow row = null;
        HSSFCell cell = null;
        int rowIndex = 2;
        row = sheet.createRow(1);
        int i = 0;
        for (String string : tableHeader) {
            cell = row.createCell(i);
            cell.setCellValue(string);
            i++;
        }
        TJicWarningDetailVo tJicWarningDetailVo = new TJicWarningDetailVo();
        List<TJicWarningDetail> list = tJicWarningSetupService.queryTJicWarningDetailss(tJicWarningDetailVo);
        for (TJicWarningDetail organ : list) {
            row = sheet.createRow(rowIndex);
            cell = row.createCell(0);
            cell.setCellValue(organ.getCreatename());

            cell = row.createCell(1);
            cell.setCellValue(organ.getNojichabatchno());

            cell = row.createCell(2);
            cell.setCellValue(organ.getRoletypelist());

            cell = row.createCell(3);
            cell.setCellValue(organ.getRoletypelistn());

            rowIndex++;
        }

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        book.write(os);
        byte[] b = os.toByteArray();
        ByteArrayInputStream in = new ByteArrayInputStream(b);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        Date date = new Date();
        String name = new String(("jicharenwuzhongxin" + sdf.format(date) + ".xls").getBytes("GBK"), "iso-8859-1");//涉及到中文问题 根据系统实际编码改变
        System.out.println("文件名称"+name);
        boolean  b1= ftpProcessor.uploadFile("/home/ftpadmin", name, in);
       // boolean  b2= ftpProcessor.uploadFile("/home/ftpadmin", name, in);
        System.out.println("上传结果B1:"+b1);
        Thread.sleep(1000);
        boolean B3=  ftpProcessor.downloadFile("/home/ftpadmin/", name,"E:/");
        System.out.println("下载结果B3:"+B3);
        Thread.sleep(1000);
    }

 

pom.xml需导入jar包

 <!--ftp文件上传-->
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.6</version>
        </dependency>
        <!--自定义连接池-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.7.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

 

FTP配置工具类:

FtpProperties.java
import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "ftp")
public class FtpProperties {

    private String  ip;
    private String  port;
    private String  username;
    private String  password;
    private Integer initialSize = 0;
    private String  encoding = "UTF-8";
    private Integer bufferSize = 4096;
    private Integer retryCount = 3;

}

 

FtpConstants.java格式接口
public interface FtpConstants {

    //ftp文件路径编码格式
    String DEFAULT_FTP_PATH_ENCODING="ISO-8859-1";

}
FtpClientPooledObjectFactory.java连接池工厂
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;

import java.io.IOException;
@Slf4j
public class FtpClientPooledObjectFactory implements PooledObjectFactory<FTPClient> {

    private FtpProperties ftpProperties;

    public FtpClientPooledObjectFactory(FtpProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    @Override
    public PooledObject<FTPClient> makeObject() throws Exception {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect(ftpProperties.getIp(), Integer.valueOf(ftpProperties.getPort()));
            ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
            log.info("连接ftp服务返回码:" + ftpClient.getReplyCode());
            ftpClient.setBufferSize(ftpProperties.getBufferSize());
            ftpClient.setControlEncoding(ftpProperties.getEncoding());
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            return new DefaultPooledObject<>(ftpClient);
        } catch (Exception e) {
            if (ftpClient.isAvailable()) {
                ftpClient.disconnect();
            }
            ftpClient = null;
            log.error("建立ftp连接失败!", (Object) e.getStackTrace());
            throw new Exception("建立ftp连接失败!", e);
        }
    }

    @Override
    public void destroyObject(PooledObject<FTPClient> pooledObject) throws Exception {
        FTPClient ftpClient = getObject(pooledObject);
        if (null != ftpClient && ftpClient.isConnected()) {
            ftpClient.disconnect();
        }
    }

    @Override
    public boolean validateObject(PooledObject<FTPClient> pooledObject) {
        FTPClient ftpClient = getObject(pooledObject);
        if (null == ftpClient || !ftpClient.isConnected()) {
            return false;
        }
        try {
            ftpClient.changeWorkingDirectory("/");
            return true;
        } catch (IOException e) {
            log.error("验证ftp连接失败!", (Object) e.getStackTrace());
            return false;
        }
    }

    @Override
    public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception {

    }

    @Override
    public void passivateObject(PooledObject<FTPClient> pooledObject) throws Exception {

    }

    private FTPClient getObject(PooledObject<FTPClient> pooledObject) {
        if (null == pooledObject || null == pooledObject.getObject()) {
            return null;
        }
        return pooledObject.getObject();
    }
}

 

FtpConfig.java连接池
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;


@Configuration
@ConditionalOnClass({GenericObjectPool.class, FTPClient.class})
@ConditionalOnProperty(prefix = "ftp", name = "isopen", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(FtpProperties.class)
public class FtpConfig {

    private Logger log = LoggerFactory.getLogger(FtpConfig.class);

    private final FtpProperties ftpProperties;

    @Autowired
    public FtpConfig(FtpProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    private ObjectPool<FTPClient> pool;

    /**
     * 预加载FTPClient连接到对象池中
     *
     * @param initialSize
     * @param maxIdle
     */
    private void preLoadingFtpClient(Integer initialSize, int maxIdle) {
        //如果初始化大小为null或者小于等于0,则不执行逻辑
        if (null == initialSize || initialSize <= 0) {
            return;
        }
        int size = Math.min(initialSize, maxIdle);
        try {
            for (int i = 0; i < size; i++) {
                pool.addObject();
            }
        } catch (Exception e) {
            log.error("预加载失败!", (Object) e.getStackTrace());
        }
    }

    /**
     * 销毁方法
     */
    @PreDestroy
    public void destroy() {
        if (null != pool) {
            pool.close();
            log.info("销毁ftp客户端连接池。。。");
        }
    }

    /**
     * 判断不存在业务Service时初始化默认Bean到Spring
     */
    @Bean
    @ConditionalOnMissingBean(FtpProcessor.class)
    public FtpProcessor ftpProcessor() {
        log.info("没有找到ftp处理器,执行创建默认处理器");
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        poolConfig.setTestWhileIdle(true);
        poolConfig.setMinEvictableIdleTimeMillis(6000);
        poolConfig.setSoftMinEvictableIdleTimeMillis(50000);
        poolConfig.setTimeBetweenEvictionRunsMillis(30000);
        pool = new GenericObjectPool<>(new FtpClientPooledObjectFactory(ftpProperties), poolConfig);
        preLoadingFtpClient(ftpProperties.getInitialSize(), poolConfig.getMaxIdle());
        DefaultFtpProcessor processor = new DefaultFtpProcessor(ftpProperties);
        processor.setFtpClientPool(pool);
        processor.setHasInit(true);
        return processor;
    }
}
DefaultFtpProcessor.java文件上传下载类
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.pool2.ObjectPool;
import org.springframework.util.Assert;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


@Getter
@Setter
@Slf4j
public class DefaultFtpProcessor implements FtpProcessor {

    private final FtpProperties ftpProperties;

    //连接池初始化标志
    private boolean hasInit = false;
    //连接池
    private ObjectPool<FTPClient> ftpClientPool;

    public DefaultFtpProcessor(FtpProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    /**
     * 上传文件
     *
     * @param path           ftp服务器保存地址
     * @param fileName       上传到ftp的文件名
     * @param originFileName 等待上传的文件名(绝对地址或路径)
     */
    @Override
    public boolean uploadFile(String path, String fileName, String originFileName) {
        boolean flag = false;
        try {
            InputStream inputStream  = new FileInputStream(new File(originFileName));
            flag=uploadFile( path,  fileName,  inputStream);
        } catch (Exception e) {
            //log.error("上传文件出错!", (Object) e.getStackTrace());
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 上传文件
     *
     * @param path        ftp服务器保存地址
     * @param fileName    上传到ftp的文件名
     * @param inputStream 文件流
     */
    @Override
    public boolean uploadFile(String path, String fileName, InputStream inputStream) throws IOException {
        boolean flag = false;
        FTPClient ftpClient = getFtpClient();
        try {
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            createDirectory(path, ftpClient);
            ftpClient.makeDirectory(path);
            ftpClient.changeWorkingDirectory(path);
            ftpClient.storeFile(fileName, inputStream);
            flag = true;
        } catch (Exception e) {
            //log.error("上传文件出错!:{}", e);
            e.printStackTrace();
        } finally {
            if(null!=inputStream) {
                inputStream.close();
            }
            releaseFtpClient(ftpClient);
        }
        return flag;
    }

    /**
     * 下载文件
     *
     * @param path      ftp服务器文件路径
     * @param fileName  文件名称
     * @param localPath 下载后的路径
     */
    @Override
    public boolean downloadFile(String path, String fileName, String localPath) {
        boolean flag = false;
        OutputStream outputStream = null;
        FTPClient ftpClient = getFtpClient();
        try {
            ftpClient.changeWorkingDirectory(path);
            FTPFile[] files = ftpClient.listFiles();
            for (FTPFile file : files) {
                if (fileName.equalsIgnoreCase(file.getName())) {
                    File localFile = new File(localPath + "/" + file.getName());
                    outputStream = new FileOutputStream(localFile);
                    ftpClient.retrieveFile(file.getName(), outputStream);
                    outputStream.close();
                }
            }
            flag = true;
        } catch (IOException e) {
            log.error("下载文件出错!", (Object) e.getStackTrace());
        } finally {
            releaseFtpClient(ftpClient);
            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("关闭输出流出错!", (Object) e.getStackTrace());
                }
            }
        }
        return flag;
    }

    /**
     * 删除文件
     *
     * @param path     ftp文件路径
     * @param fileName 文件名
     */
    @Override
    public boolean deleteFile(String path, String fileName) {
        boolean flag = false;
        FTPClient ftpClient = getFtpClient();
        try {
            ftpClient.changeWorkingDirectory(path);
            ftpClient.dele(fileName);
            ftpClient.logout();
            flag = true;
        } catch (IOException e) {
            log.error("删除文件出错!", (Object) e.getStackTrace());
        } finally {
            releaseFtpClient(ftpClient);
        }
        return flag;
    }

    /**
     * 创建多层目录,如果ftp服务器已存在该目录,则不创建,如果没有,则创建
     *
     * @param remote    创建的目录
     * @param ftpClient
     */
    @Override
    public boolean createDirectory(String remote, FTPClient ftpClient) throws IOException {
        String directory = remote + "/";
        //如果远程目录不存在,则递归创建远程目录
        if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(directory, ftpClient)) {
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            }
            end = directory.indexOf("/", start);
            String path = "";
            String paths = "";
            do {
                String subDirectory = new String(remote.substring(start, end).getBytes(ftpProperties.getEncoding()), FtpConstants.DEFAULT_FTP_PATH_ENCODING);
                path = path + "/" + subDirectory;
                if (!existFile(path, ftpClient)) {
                    if (makeDirectory(subDirectory, ftpClient)) {
                        changeWorkingDirectory(subDirectory, ftpClient);
                    } else {
                        log.warn("创建目录[" + subDirectory + "]失败");
                        changeWorkingDirectory(subDirectory, ftpClient);
                    }
                } else {
                    changeWorkingDirectory(subDirectory, ftpClient);
                }
                paths = paths + "/" + subDirectory;
                start = end + 1;
                end = directory.indexOf("/", start);
            } while (end <= start);
        }
        return true;
    }

    /**
     * 判断ftp服务器的路径或文件是否存在
     *
     * @param path
     * @param ftpClient
     */
    @Override
    public boolean existFile(String path, FTPClient ftpClient) throws IOException {
        boolean flag = false;
        FTPFile[] files = ftpClient.listFiles(path);
        if (files.length > 0) {
            flag = true;
        }
        return flag;
    }

    /**
     * 创建目录
     *
     * @param directory
     * @param ftpClient
     */
    @Override
    public boolean makeDirectory(String directory, FTPClient ftpClient) {
        boolean flag = true;
        try {
            flag = ftpClient.makeDirectory(directory);
            if (flag) {
                log.info("创建文件夹:" + directory);
            }
        } catch (IOException e) {
            log.error("创建文件夹" + directory + "失败!", (Object) e.getStackTrace());
        }
        return flag;
    }

    /**
     * 切换目录
     *
     * @param directory 要切换的目录
     * @param ftpClient ftp客户端
     */
    public boolean changeWorkingDirectory(String directory, FTPClient ftpClient) {
        boolean flag = true;
        try {
            flag = ftpClient.changeWorkingDirectory(directory);
            if (flag) {
                log.info("进入文件夹:" + directory);
            }
        } catch (IOException e) {
            log.error("进入文件夹:"+directory+"错误!", (Object) e.getStackTrace());
        }
        return flag;
    }

    /**
     * 按行读取FTP文件
     *
     * @param remoteFilePath ftp路径
     */
    public List<String> readFileByLine(String remoteFilePath) throws IOException {
        FTPClient ftpClient = getFtpClient();
        try (InputStream inputStream = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            return reader.lines()
                    .map(line -> StringUtils.trimToEmpty(line))
                    .filter(line -> StringUtils.isNotEmpty(line))
                    .collect(Collectors.toList());
        } finally {
            ftpClient.completePendingCommand();
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 获取指定路径下的ftp文件
     *
     * @param remotePath 指定路径
     */
    public FTPFile[] retrieveFtpFiles(String remotePath) throws IOException {
        FTPClient ftpClient = getFtpClient();
        try {
            return ftpClient.listFiles(encodingPath(remotePath + "/"), file -> file != null && file.getSize() > 0);
        } finally {
            releaseFtpClient(ftpClient);
        }
    }

    /**
     * 获取指定ftp路径下的文件名称
     *
     * @param remotePath 指定ftp路径
     */
    @Override
    public List<String> retrieveFileNames(String remotePath) throws IOException {
        FTPFile[] files = retrieveFtpFiles(remotePath);
        if (ArrayUtils.isEmpty(files)) {
            return new ArrayList<>();
        }
        return Arrays.stream(files).filter(Objects::nonNull).map(FTPFile::getName).collect(Collectors.toList());
    }

    /**
     * 获取编码后的文件路径
     */
    private String encodingPath(String path) throws UnsupportedEncodingException {
        //在FTP协议中,规定文件名编码格式为ISO-8859-1,所以目录名或文件名需要转码
        return new String(path.replaceAll("//", "/").getBytes(ftpProperties.getEncoding()), FtpConstants.DEFAULT_FTP_PATH_ENCODING);
    }

    /**
     * 获取ftp客户端
     */
    private FTPClient getFtpClient() {
        checkFtpClientPoolAvailable();
        FTPClient ftpClient = null;
        Exception exception = null;
        //获取连接,做多尝试n次
        try {
            for (int i = 0; i < ftpProperties.getRetryCount(); i++) {
                ftpClient = ftpClientPool.borrowObject();
                ftpClient.enterLocalPassiveMode();//设置为被动模式
                ftpClient.changeWorkingDirectory("/");
                break;
            }
        } catch (Exception e) {
            log.error("无法在连接池中获取ftp客户端!", (Object) e.getStackTrace());
            exception = e;
        }
        if (null == ftpClient) {
            throw new RuntimeException("无法在连接池中获取ftp客户端", exception);
        }
        return ftpClient;
    }

    /**
     * 释放ftp客户端
     *
     * @param ftpClient
     */
    private void releaseFtpClient(FTPClient ftpClient) {
        if (null != ftpClient) {
            try {
                //从ftp连接池中移除ftp客户端
                ftpClientPool.returnObject(ftpClient);
            } catch (Exception e) {
                try {
                    //判断客户端是否可用
                    if (ftpClient.isAvailable()) {
                        //销毁连接
                        ftpClient.disconnect();
                    }
                } catch (IOException ex) {
                    log.error("销毁ftp连接失败!", (Object) e.getStackTrace());
                }
                log.error("从ftp连接池移除ftp客户端失败!", (Object) e.getStackTrace());
            }
        }
    }

    /**
     * 检查ftp连接池是否可用
     */
    private void checkFtpClientPoolAvailable() {
        Assert.state(hasInit, "ftp未启用或连接失败!");
    }
}