使用Apache Camel引擎实现文件自动解压读取

业务需求、流程介绍
由于需要对用户画像、产品画像等数据进行统计分析,方便运营人员根据统计结果针对APP提出改进方案。APP采用数据埋点方式,根据每个用户200条或半个小时的规则使用zip压缩文件方式上传到文件服务器。后台需要对上传的压缩文件进行解压读取保存到后台mongoDB。

埋点结构:
{
  "id": "【移动端访问埋点记录唯一标识】xxx-sdfsdwr-sdw34-sdewsfdsr-3434",
  "client": {
    "access": {
      "system": "【设备系统】Android 10",
	  "channel": "【渠道】xiaomi\huawei\appstore",
      "model": "【设备型号】HUAWEI HMA-AL00",
      "deviceId": "【设备标识】ffffffff-ccaa-a233-ffff-ffffef05ac4a",
      "appId": "【APP标识】szcs",
      "time": "【埋点时间,数字类型毫秒时间戳】1617775629402",
      "co": "【经纬度坐标,逗号分隔,按照用户隐私协议默认情况不回传】31.2807691689,112.5382624525",
      "version": "【APP版本】1.0.0",
	  "net":"【网络类型net】2G\3G\4G",
	  "netOperator":"【网络运营商】中国电信\中国移动\中国联通"
    }.......

考虑camel允许用户定义灵活的路由规则,实现各应用系统之间交互。减少并发量。决定使用camel来实现。

camel简介
camel是一款基于规则快速实现消息流转的开发组件,集成该组件后,你的程序可以编写最少的代码实现复杂的消息在不同的协议规则间流转。
程序实现从Ftp获得.xml文件,然后将收到的文件内容值转换后,发送到Jms Queue中,并且将Request写入到数据库log表。

Ftp组件->Jms组件->Db组件

只需要短短的几行代码就可以实现这样一个功能,但是如果用其他框架一个个功能的写,将会有非常多的代码量并且可能会出现一些纰漏,而camel已经将这些功能都封装在camel组件中了,节省开发成本。

业务实现
1.引入相关jar包

<!--Apache Camel-->
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-spring-boot-starter</artifactId>
    <version>3.0.0-M1</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.4.9</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.5</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>

2.app通过文件上传服务上传压缩文件到指定文件夹。

#yml配置
access:
  #埋点压缩文件目录
  zipFile: /data/analysis/zipFile
  #解压文件目录
  unzip: /data/analysis/unzip
/**
 * 埋点数据批量提交
 */
@ApiOperation("埋点数据批量提交")
@PostMapping(value = "/v1/batchPush", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ViewResult push(@RequestPart(value = "file") MultipartFile file) {
    // 获取文件名
    String fileName = file.getOriginalFilename();
    // 新建一个文件对象
    File dest = new File(new File(zipFilePath).getAbsolutePath() + File.separator + fileName);
    // 如果目的地址没有创建,那么就创建
    if (!dest.getParentFile().exists()) {
        dest.getParentFile().mkdirs();
    }
    try {
        // 保存文件
        file.transferTo(dest);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return ViewResult.success();
}

3.创建下载路由,定义路由规则使上传文件夹复制到指定文件夹

/**
 * 下载路由
 */
@Component
public class FileUploaderRoute extends RouteBuilder {
    @Autowired
    private LocationFileProcessor locationFileProcessor;

    @Value(value = "${access.zipFile:}")
    private String zipFilePath;
    @Value(value = "${access.unzip:}")
    private String unzipFilePath;
    @Override
    public void configure() {
        // 新建文件对象
        String bizPath = "file";
        File dest = new File(zipFilePath + File.separator + bizPath );
        File unDest = new File(unzipFilePath + File.separator + bizPath);
        // 如果目的地址没有创建,那么就创建
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        if (!unDest.getParentFile().exists()) {
            unDest.getParentFile().mkdirs();
        }
        //添加完成后自动删除参数
        String uploadZip = "file:"+ zipFilePath+File.separator+"?delete=true";
        //复制的目录路径
        String unzip = "file:"+ unzipFilePath;
        //定义路由规则 压缩文件自动复制
        from(uploadZip).to(unzip).process(locationFileProcessor);
    }

}

4.创建路由解析类,对压缩文件解压,获取文件信息并进行数据转换。通过@FeignClient调用远程保存接口。

/**
 * 路由解析类
 */
@Slf4j
@Component
public class LocationFileProcessor implements Processor {

    @Value(value = "${access.unzip:}")
    private String unzipFilePath;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RemoteTrackDataService remoteTrackDataService;

    @Override
    public void process(Exchange exchange){
        log.info("=============开始解析zip文件。。。");
        //文件解压
        GenericFileMessage<RandomAccessFile> inFileMessage = (GenericFileMessage<RandomAccessFile>) exchange.getIn();
        String fileName = inFileMessage.getGenericFile().getFileName();
        File zipFile = new File(unzipFilePath + File.separator + fileName);
        //获取用户信息
        Long userId = redisService.getCacheObject(fileName);
        //解压文件
        ZipUtil.unzip(zipFile);
        //获取由camel路由解压后文件
        List<File> files = FileEnhanceUtils.listFilesInDirWithFilter(unzipFilePath,"txt",true);
        List<MgAppAccessTrackData.Client> clientArrayList = new ArrayList<>();
        for (File fil : files) {
                List<MgAppAccessTrackData.Client> list =
                        JSONArray.parseArray(txt2String(fil),MgAppAccessTrackData.Client.class);
                for (MgAppAccessTrackData.Client client : list) {
                    log.info("=============埋点数据转换成功。。。");
                    clientArrayList.add(client);
                }
        }
        MgClientUserDto userDto = new MgClientUserDto();
        userDto.setList(clientArrayList);
        userDto.setUerId(userId);
        if(userId!=null){
            //获取ip信息
            userDto.setUserIp(redisService.getCacheObject(userId.toString()));
        }
        try {
            //调用app对外服务保存到mongoDB
            R<Boolean> r = remoteTrackDataService.saveMongoDB(userDto);
            log.info("=============埋点数据调用app"+r.getMessage()+"。。。");
            if ("操作成功".equals(r.getMessage())){
                //删除目录下的所有文件
                log.info("=============删除目录下的所有文件。。。");
                FileEnhanceUtils.deleteFilesInDir(unzipFilePath);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 读取txt文件的内容
     * @param file 想要读取的文件对象
     * @return 返回文件内容
     */
    public static String txt2String(File file){
        StringBuilder result = new StringBuilder();
        try{
            //构造一个BufferedReader类来读取文件
            BufferedReader br = new BufferedReader(new FileReader(file));
            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                result.append(System.lineSeparator()+s);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result.toString();
    }
}

这里就完成了,另外camel支持异常处理,支持"异步重试,延迟重试"等多种处理方式。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冫时光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值