手摸手系列之---camel ftp监听接收解析xml报文并入库生成Java对象实战

本文介绍如何使用Apache Camel的FTP组件,监听FTP服务器上的XML文件,接收到文件后进行解析,并将数据入库。文章详细讲解了配置依赖、文件过滤、异常处理和核心路由代码,以及数据处理逻辑。
摘要由CSDN通过智能技术生成
前言

版本:
SpringBoot 2.4
camel 3.5.0

最近在做跟一个第三方系统的对接,主要流程就是对方生成XML格式的报文,需要我方将其报文发送到海关申报,然后将申报完的数据再组装成XML报文格式发回到对方的FTP服务器。功能其实挺简单,用Apache的camel-ftp很容易就能实现,下面看看具体如何做吧。

一、引入camel依赖:
<!-- camel-spring-boot-starter -->
<dependency>
	<groupId>org.apache.camel.springboot</groupId>
	<artifactId>camel-spring-boot-starter</artifactId>
	<version>3.5.0</version>
</dependency>
<!-- camel ftp组件-->
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-ftp</artifactId>
    <version>3.5.0</version>
</dependency>
二、camel配置:
# camel配置
camel:
  # camel ftp服务路由地址
  route:
    id: XMLRoute
    ftp:
      server: sftp://119.22.77.152:22/Export/Pre-CDF?username=root&password=123456&passiveMode=true&move=backup&moveFailed=error&delay=5000&exceptionHandler=#errorExceptionHandler&onCompletionExceptionHandler=#errorExceptionHandler
	# server: file:e:/test?recursive=true&move=.backup&moveFailed=.error&exceptionHandler=#errorExceptionHandler&onCompletionExceptionHandler=#errorExceptionHandler
  file:
    download:
      local:
        # camel 文件内容下载到本地地址(配置中文乱码)
        address: file:/home/platform/ex-dec/receive/
        # camel 文件下载到本地备份地址
        backup:
          address: file:/home/platform/ex-dec/receive/backup/
  • [IP][端口]后面的/Export/Pre-CDF表示监听/Export/Pre-CDF目录。
  • passiveMode=true:被动模式true
  • move=backup:将源目录下的源文件移动到backup目录下,如果不设置默认是移动到.camel目录中。
  • moveFailed=error:将异常导致失败的源文件移动到error目录中。
  • delay=5000:表示5秒监听一次。
  • exceptionHandler:自定义的异常处理器。
三、新建文件过滤类FtpDownloadFileFilter,继承camel的Predicate
package com.yorma.ex.camel.filter;

import lombok.extern.slf4j.Slf4j;
import org.apache.camel.Exchange;
import org.apache.camel.Predicate;
import org.apache.camel.component.file.GenericFileMessage;
import org.springframework.stereotype.Component;

import java.io.RandomAccessFile;

/**
 * camel过滤器
 *
 * @author ZHANGCHAO
 * @date 2021/6/8 14:27
 * @since 1.0.0
 */
@Slf4j
@Component
public class FtpDownloadFileFilter implements Predicate {
   

    @Override
    public boolean matches(Exchange exchange) {
   
        boolean bMatches;
        GenericFileMessage<RandomAccessFile> inFileMessage = (GenericFileMessage<RandomAccessFile>) exchange.getIn();
        String fileName = inFileMessage.getGenericFile().getFileName();
       	// 只需要处理.xml格式的文件
        bMatches = fileName.endsWith(".xml");
        log.info("fileName:"+fileName+", matches:"+bMatches);
        return bMatches;
    }
}

四、自定义异常处理类ErrorExceptionHandler,继承camel的ExceptionHandler
package com.yorma.ex.camel.handle;

import lombok.extern.slf4j.Slf4j;
import org.apache.camel.Exchange;
import org.apache.camel.spi.ExceptionHandler;
import org.apache.camel.spring.SpringCamelContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 错误处理
 *
 * @author ZHANGCHAO
 * @date 2021/6/9 8:44
 * @since 1.0.0
 */
@Slf4j
@Component
public class ErrorExceptionHandler implements ExceptionHandler {
   

    /**
     * camel路由ID
     */
    @Value("${camel.route.id}")
    private String routeId;

    @Override
    public void handleException(Throwable exception) {
   
        this.handleException(null, null, exception);
    }

    @Override
    public void handleException(String message, Throwable exception) {
   
        this.handleException(message, null, exception);
    }

    @Override
    public void handleException(String message, Exchange exchange, Throwable exception) {
   
        log.info("系统在处理下行数据时出现异常,获取Exchange对象是否为空:{},异常信息是:", null != exchange ? "不为空" : "为空", exception);
        if (null != exchange) {
   
            if (exchange.getContext() instanceof SpringCamelContext) {
   
                SpringCamelContext springCamelContext = (SpringCamelContext) exchange.getContext();
                // 关闭路由
                stop(springCamelContext);
                // 启动路由
                start(springCamelContext);
            } else {
   
                log.info("系统在处理下行数据时出现异常,获取getContext不是SpringCamelContext");
                exchange.getFromEndpoint().stop();
                exchange.getContext().stop();
            }
        }
    }

    /**
     * 关闭路由
     *
     * @param springCamelContext SpringCamelContext对象
     */
    private void stop(SpringCamelContext springCamelContext) {
   
        try {
   
            log.info("路由ID:{},即将关闭", routeId);
            springCamelContext.stopRoute(routeId);
            log.info("路由ID:{},关闭完成", routeId);
        } catch (Exception e) {
   
            log.error("路由ID:{},在关闭路由时出现异常,异常信息是:", routeId, e);
        }
    }

    /**
     * 启动路由
     *
     * @param springCamelContext SpringCamelContext对象
     */
    private void start(SpringCamelContext springCamelContext) {
   
        // 获取当前时间
        long currentTime = System.currentTimeMillis();
        // 设置1小时后执行
        currentTime += 60 * 60 * 1000;
        Date date = new Date(currentTime);
        log.info("路由ID:{},系统将在:{},启动此路由", routeId, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
        // 启动路由
        start(springCamelContext, date);
    }

    /**
     * 指定时间启动路由
     *
     * @param springCamelContext SpringCamelContext对象
     * @param startDate          启动时间
     */
    private void start(SpringCamelContext springCamelContext, Date startDate) {
   
        new Timer().schedule(new TimerTask() {
   
            @Override
            public void run() {
   
                try {
   
                    log.info("路由ID:{},即将启动", routeId);
                    springCamelContext.startRoute(routeId);
                    log.info("路由ID:{},启动完成", routeId);
                } catch (Exception e) {
   
                    log.error("路由ID:{},在启动路由时出现异常,异常信息是:", routeId, e);
                }
            }
        }, startDate);
    }
}
五、核心代码,创建camel的路由
package com.yorma.ex.camel.route;

import com.yorma.ex.camel.filter.FtpDownloadFileFilter;
import com.yorma.ex.camel.process.DataProcessor;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class CamelRouteBuilder extends RouteBuilder {
   

	/**
	 * FTP服务器地址
	 */
	@Value("${camel.route.ftp.server}")
	private String ftpServer;

	/**
	 * 文件下载根路径
	 */
	@Value("${camel.file.download.local.address}")
	private String basePath;

	/**
	 * 备份目录根目录
	 */
	@Value("${camel.file.download.local.backup.address}")
	private String backupBasePath;

	@Autowired
	private DataProcessor dataProcessor;

	@Autowired
	private FtpDownloadFileFilter ftpDownloadFileFilter;

	@Override
	public void configure() {
   
		try {
   
			from(ftpServer)
					.log("原始xml文件: ${file:name}开始处理")
					.filter(ftpDownloadFileFilter)
					.toD(basePath)
//					.toD(backupBasePath)
					.log("原始xml文件: ${file:name}下载成功")
					.process(dataProcessor);
		} catch (Exception e) {
   
			log.info("系统路由在执行任务时发生异常,异常信息是:", e);
		}
	}
}
六、新增数据处理类DataProcessor实现Processor ,并重写void process(Exchange exchange) 方法,用于camel取回文件后的后续处理。
package com.yorma.ex.camel.process;

import cn.hutool.core.io.IoUtil;
import com.yorma.dcl.api.DecApi;
import com.yorma.dcl.entity.DecHead;
import com.yorma.entity.YmMsg;
import com.yorma.ex.utils.JSONObject;
import com.yorma.ex.utils.XML;
import com.yorma.util.RequestKit;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import static cn.hutool.core.util.ObjectUtil.isNotEmpty;
import static cn.hutool.core.util.StrUtil.isBlank;
import static com.yorma.ex.utils.XmlUtils.setDecFromXML;

/**
 * 文件处理
 *
 * @author Administrator
 */
@Slf4j
@Component
public class DataProcessor implements Processor {
   

    @Autowired
    private DecApi decApi;

    /**
     * 文件下载根路径
     */
    @Value("${camel.file.download.local.address}")
    private String basePath;

    @Override
    public void process(Exchange exchange) throws IOException {
   
        // 获取文件名称
        String fileName = String.valueOf(exchange.getIn().getHeader(Exchange.FILE_NAME));
        if (isBlank(fileName)) {
   
            log.info("获取文件名称为空或空字符串,系统终止操作");
            return;
        }
        File file = new File(basePath.replaceFirst("file:", "") + fileName);
        if (!file.exists()) {
   
            log.info("未查找到下载的本地报文!");
            return;
        }
        InputStream in = new FileInputStream(file);
        String xml = IoUtil.read(in, StandardCharsets.UTF_8);
        in.close();
        JSONObject jsonObject = XML.toJSONObject(xml);
        System.out.println("jsonObject==> " + jsonObject);
        DecHead decHead = new DecHead();
        setDecFromXML(decHead, jsonObject);
        RequestKit.addHeader("apiKey", "461277322-943d-4b2f-b9b6-3f860d746ffd");
        RequestKit.addHeader("apiUser", "exDecUser");
        RequestKit.addHeader("Tenant-Id", "1310053879995457537");
        RequestKit.addHeader("Customer-Id", "1310053881811587074");
        YmMsg<DecHead> decHeadYmMsg = decApi.saveDecForEx(decHead);
        if (isNotEmpty(decHeadYmMsg.getData())) {
   
            DecHead resultHead = decHeadYmMsg.getData();
            log.info("===> 已生成报关单草单" + resultHead.toString());
        }
    }
}

思路:将文件转为JSONObject,然后从里面get取值。
主要取值逻辑代码:

package com.yorma.ex.utils;

import cn.hutool.core.collection.CollUtil;
import cn
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值