背景:需要定时将服务器下的日志文件上传到指定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;
}
}