FTP-读取指定目录下的文件,上传到FTP服务器,一键复制黏贴,就是这么丝滑~

背景:需要定时将服务器下的日志文件上传到指定FTP服务器的目录下,并通知第三方平台文件已上传。

FTP服务器模拟工具:

 application.yml配置:

spring:
  logfilepath: /home/jboss/server/default/log
  # ftp配置
  ftp:
    ip: 192.*****
    port: 21
    username: root
    password: 123456
    path: /FtpRoot/ZZDT12/
    subsystem: CCTV/
  # 智能运维平台配置
  znyw:
    # ftp文件上传后通知智能运维系统地址
    filestatusurl: http://***:7000/imaster/log/upload
    # 日志文件每天 23 点 58 分执行上传、通知智能运维平台一次
    scan: "0 58 23 * * ?"
    # 线路编码
    lineid: 12
    # 子系统编码
    subsystemid: 10
  servlet:
    multipart:
      enabled: true
      # 根据实际需求作调整
      max-file-size: -1
      max-request-size: -1

定时任务:

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.vcom.nms.service.IFileUploadService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/**
 * 运维对接-日志文件上报接口
 */
@Configuration
public class LogFilePushTask extends BaseTask {

    private static final Logger logger = LoggerFactory.getLogger(LogFilePushTask.class);

    @Autowired
    private IFileUploadService fileUploadService;

    @Value("${spring.logfilepath}")
    private String SYS_LOG_FILE_PATH;

    /**
     * 日志文件每天 23 点 58 分执行上传、通知智能运维平台一次
     */
    @Scheduled(cron = "${spring.znyw.scan}")
    private void fileUploadTask() {
        logger.info("============come in fileUploadTask============");
        List<MultipartFile> multipartFileList = new ArrayList<>();
        try {
            multipartFileList = getFilesAsMultipart(SYS_LOG_FILE_PATH);
        } catch (IOException e) {
            logger.error("io Exc:", e);
        } catch (Exception e) {
            logger.error("Exc:", e);
        }
        logger.info("此次上传文件共:" + multipartFileList.size());
        multipartFileList.forEach(file -> {
            String fileName = fileUploadService.uploadFileAndNotice(file);
            if (StringUtils.isNotBlank(fileName)) {
                logger.info("成功上传日志文件:" + fileName);
            }
        });
    }

    public static List<MultipartFile> getFilesAsMultipart(String directoryPath) throws IOException {
        List<MultipartFile> multipartFiles = new ArrayList<>();
        File directory = new File(directoryPath);
        File[] files = directory.listFiles();
        for (File file : files) {
            multipartFiles.add(convertToMultipart(file));
        }
        return multipartFiles;
    }

    private static MultipartFile convertToMultipart(File file) throws IOException {
        byte[] fileContent = Files.readAllBytes(file.toPath());
        return new MockMultipartFile(file.getName(), file.getName(), "text/plain", fileContent);
    }

}

接口类:

import org.springframework.web.multipart.MultipartFile;

import java.util.List;

public interface IFileUploadService {
    /**
     * @Description:  上传文件到ftp服务器并通知智能运维平台
     * @param file
     * @return:
     * @Author: hyh
     * @Date: 2023/4/19 9:19
     */
    String uploadFileAndNotice(MultipartFile file);

    /**
     * @Description:  通知智能运维平台文件上传
     * @param fileName
     * @return:
     * @Author: hyh
     * @Date: 2023/4/19 11:13
     */
    void noticZnywFileStatus(String fileName);

    /**
     * @Description:  根据文件名称查看是否成功上传
     * @param fileName
     * @return:
     * @Author: hyh
     * @Date: 2023/4/19 13:28
     */
    List getFileNameList(String fileName);
}

工具类FtpUtil:

package com.vcom.nms.utils;


import com.vcom.nms.config.FtpConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description:  FtpUtil工具类
 * @return:
 * @Author: hyh
 * @Date: 2023/4/18 15:11
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class FtpUtil {

    private final FtpConfig ftpConfig;

    private static final String DIR_SPLIT = "/";

    /**
     * 获取 FTPClient
     *
     * @return FTPClient
     */
    private FTPClient getFTPClient() {
        FTPClient ftpClient = ftpConfig.connect();
        if (ftpClient == null || !FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
            throw new RuntimeException("ftp客户端异常");
        }
        return ftpClient;
    }

    /**
     * 获取FTP某一特定目录下的所有文件名称
     *
     * @param ftpDirPath FTP上的目标文件路径
     */
    public List<String> getFileNameList(String ftpDirPath) {
        FTPClient ftpClient = getFTPClient();
        try {
            // 通过提供的文件路径获取 FTPFile 文件列表
            // FTPFile[] ftpFiles = ftpClient.listFiles(ftpDirPath, FTPFile::isFile); // 只获取文件
            // FTPFile[] ftpFiles = ftpClient.listFiles(ftpDirPath, FTPFile::isDirectory); // 只获取目录
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpDirPath);
            if (ftpFiles != null && ftpFiles.length > 0) {
                return Arrays.stream(ftpFiles).map(FTPFile::getName).collect(Collectors.toList());
            }
            log.error(String.format("路径有误,或目录【%s】为空", ftpDirPath));
        } catch (IOException e) {
            log.error("文件获取异常:", e);
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 上传文件
     *
     * @param uploadPath 上传路径
     * @param fileName   文件名
     * @param input      文件输入流
     * @return 上传结果
     */
    public boolean upload(String uploadPath, String fileName, InputStream input) {
        FTPClient ftpClient = getFTPClient();
        try {
            //把文件转换为二进制字符流的形式进行上传
            ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
            createDir(ftpClient, uploadPath);
            // 文件写入
            boolean storeFile = ftpClient.storeFile(fileName, input);
            if (storeFile) {
                log.info("文件:{}上传成功", fileName);
            } else {
                throw new RuntimeException("ftp文件写入异常");
            }
            ftpClient.logout();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
                if (ftpClient.isConnected()) {
                    ftpClient.disconnect();
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
        return false;
    }

    // 创建文件夹,并切换到该文件夹
    // 比如: hello/test
    //最终会切换到test 文件夹返回
    private void createDir(FTPClient client, String path) throws IOException {
        String[] dirs = path.split("/");
        for (String dir : dirs) {
            if (StringUtils.isEmpty(dir)) {
                continue;
            }
            if (!client.changeWorkingDirectory(dir)) {
                client.makeDirectory(dir);
            }
            client.changeWorkingDirectory(dir);
        }
    }
    /**
     * 下载文件 *
     *
     * @param ftpPath     FTP服务器文件目录 *
     * @param ftpFileName 文件名称 *
     * @param localPath   下载后的文件路径 *
     * @return
     */
    public boolean download(String ftpPath, String ftpFileName, String localPath) {
        FTPClient ftpClient = getFTPClient();
        OutputStream outputStream = null;
        try {
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.isFile() && file.getName().equals(ftpFileName));
            if (ftpFiles != null && ftpFiles.length > 0) {
                FTPFile ftpFile = ftpFiles[0];
                File localFile = new File(localPath + DIR_SPLIT + ftpFile.getName());
                // 判断本地路径目录是否存在,不存在则创建
                if (!localFile.getParentFile().exists()) {
                    localFile.getParentFile().mkdirs();
                }
                outputStream = Files.newOutputStream(localFile.toPath());
                ftpClient.retrieveFile(ftpFile.getName(), outputStream);

                log.info("fileName:{},size:{}", ftpFile.getName(), ftpFile.getSize());
                log.info("下载文件成功...");
                return true;
            } else {
                log.info("文件不存在,filePathname:{},", ftpPath + DIR_SPLIT + ftpFileName);
            }
        } catch (Exception e) {
            log.error("下载文件失败...");
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 从FTP服务器删除文件或目录
     * 存在文件的目录无法删除
     *
     * @param ftpPath  服务器文件存储路径
     * @param fileName 服务器文件存储名称
     * @return 删除结果
     */
    public boolean delete(String ftpPath, String fileName) {
        FTPClient ftpClient = getFTPClient();
        try {
            // 在 ftp 目录下获取文件名与 fileName 匹配的文件信息
            FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.getName().equals(fileName));
            // 删除文件
            if (ftpFiles != null && ftpFiles.length > 0) {
                boolean del;
                String deleteFilePath = ftpPath + DIR_SPLIT + fileName;
                FTPFile ftpFile = ftpFiles[0];
                if (ftpFile.isDirectory()) {
                    del = ftpClient.removeDirectory(deleteFilePath);
                } else {
                    del = ftpClient.deleteFile(deleteFilePath);
                }
                log.info(del ? "文件:{}删除成功" : "文件:{}删除失败", fileName);
                return del;
            } else {
                log.warn("文件:{}未找到", fileName);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 判断文件大小
     *
     * @param len
     *            文件长度
     * @param size
     *            限制大小
     * @param unit
     *            限制单位(B,K,M,G)
     * @return
     */
    public static boolean checkFileSize(Long len, int size, String unit) {
        double fileSize = 0;
        if ("B".equals(unit.toUpperCase())) {
            fileSize = (double) len;
        } else if ("K".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1024;
        } else if ("M".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1048576;
        } else if ("G".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1073741824;
        }
        if (fileSize > size) {
            return false;
        }
        return true;
    }
}


 

接口实现类:

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class FileUploadServiceImpl implements IFileUploadService {
    private static final Logger logger = LoggerFactory.getLogger(FileUploadServiceImpl.class);

    @Autowired
    private final FtpUtil ftpUtil;
    @Value("${spring.ftp.path}")
    private String FTP_ROOT_DIR;
    @Value("${spring.ftp.subsystem}")
    private String SUBSYSTEM_NAME;
    @Value("${spring.znyw.filestatusurl}")
    private String ZNYW_FILE_STATUS_URL;
    @Value("${spring.znyw.lineid}")
    private int ZNYW_LINEID;
    @Value("${spring.znyw.subsystemid}")
    private int ZNYW_SUBSYSTEMID;

    private FileUploadServiceImpl(FtpUtil ftpUtil) {
        this.ftpUtil = ftpUtil;
    }

    @Override
    public String uploadFileAndNotice(MultipartFile file) {
        if (!ftpUtil.checkFileSize(file.getSize(), 100, "M")) {
            logger.warn("文件{}大小超过100MB,不予上传!", file.getOriginalFilename());
            return null;
        }
        String fileName = "";
        try {
            // 读取文件信息
            String name = file.getOriginalFilename();
            InputStream fileInputStream = file.getInputStream();
            //年月日
            LocalDate date = LocalDate.now();
            DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyyMMdd");
            DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy_MM_dd");
            fileName = date.format(formatter2) + "_" + name;
            // 上传文件到 Ftp 服务
            boolean flag = ftpUtil.upload(FTP_ROOT_DIR + SUBSYSTEM_NAME + date.format(formatter1), fileName, fileInputStream);
            //通知智能运维平台
            if (!(flag && syncMsgToZnywPlatform(fileName))) {
                logger.error(flag + ":{}文件上传失败!", file.getOriginalFilename());
                return null;
            }
        } catch (IOException e) {
            logger.error("文件上传失败!", String.valueOf(e));
        }
        return fileName;
    }

    @Override
    public void noticZnywFileStatus(String fileName) {
        syncMsgToZnywPlatform(fileName);
    }

    @Override
    public List getFileNameList(String fileName) {
        Pattern pattern = Pattern.compile("\\d{4}_\\d{2}_\\d{2}");
        Matcher matcher = pattern.matcher(fileName);
        String directory = "";
        if (matcher.find()) {
            directory = matcher.group().replace("_", "");
        } else {
            throw new RuntimeException("文件名称格式不正确!");
        }
        String path = FTP_ROOT_DIR + SUBSYSTEM_NAME + directory;
        return ftpUtil.getFileNameList(path);
    }

    //同步ftp文件上传信息到智能运维平台
    public boolean syncMsgToZnywPlatform(String fileName) {
        Response response = null;
        long timestamp = System.currentTimeMillis();
        try {
            OkHttpClient client = new OkHttpClient().newBuilder()
                    .connectTimeout(15, TimeUnit.SECONDS)
                    .readTimeout(15, TimeUnit.SECONDS)
                    .writeTimeout(15, TimeUnit.SECONDS)
                    .build();
            MediaType mediaType = MediaType.parse("text/plain");
            RequestBody body = RequestBody.create(mediaType, "");
            Request request = new Request.Builder()
                    .url(ZNYW_FILE_STATUS_URL + "?lineId=" + ZNYW_LINEID + "&systemId=" + ZNYW_SUBSYSTEMID + "&fileName=" + fileName)
                    .method("POST", body)
                    .addHeader("Content-Type", "application/json")
                    .build();
            response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                String res = response.body().string();
                logger.info("操作:通知智能运维系统,请求客户端返回信息:{}", res);
                return true;
            }
        } catch (Exception e) {
            logger.error("IOException={}", e.getMessage());
            return false;
        } finally {
            if (null != response) {
                response.close();
            }
            logger.info("fileStatusUpload_time:" + (System.currentTimeMillis() - timestamp));
        }
        return false;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值