当前位置: 首页 > news >正文

DataCap 自定义 File 转换器

DataCap 支持自定义 File 转换器,使用者可以编写自己的文件转换器集成到 DataCap 中。该文档主要讲解如何快速集成一个文件转换器到 DataCap 系统中。

该模块我们主要使用到的是 file 模块内的代码,我们本文使用 json 来做示例。

模块基本配置


新建项目后在 pom.xml 文件中增加以下内容:

<dependencies><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-reflect</artifactId></dependency><dependency><groupId>com.google.inject</groupId><artifactId>guice</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></dependency><dependency><groupId>io.edurt.datacap</groupId><artifactId>datacap-file-spi</artifactId></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId></dependency>
</dependencies><build><plugins><plugin><groupId>org.jetbrains.dokka</groupId><artifactId>dokka-maven-plugin</artifactId></plugin></plugins>
</build>

我们添加 datacap-file-spi 依赖,这样我们就可以实现集成文件转换器。

Json Module 加载器


package io.edurt.datacap.file.jsonimport com.google.inject.multibindings.Multibinder
import io.edurt.datacap.file.File
import io.edurt.datacap.file.FileModuleclass JsonModule : FileModule()
{override fun configure(){Multibinder.newSetBinder(this.binder(), File::class.java).addBinding().to(JsonFile::class.java)}
}

Json File 转换器


package io.edurt.datacap.file.jsonimport com.fasterxml.jackson.core.JsonEncoding
import com.fasterxml.jackson.core.JsonFactory
import com.fasterxml.jackson.core.JsonGenerationException
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
import io.edurt.datacap.common.utils.DateUtils
import io.edurt.datacap.file.File
import io.edurt.datacap.file.FileConvert.formatFile
import io.edurt.datacap.file.model.FileRequest
import io.edurt.datacap.file.model.FileResponse
import org.slf4j.LoggerFactory.getLogger
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.util.Objects.requireNonNullclass JsonFile : File
{private val log = getLogger(this::class.java)override fun format(request: FileRequest): FileResponse{val response = FileResponse()try{log.info("${name()} format start time [ ${DateUtils.now()} ]")log.info("${name()} format headers start")response.headers = request.headerslog.info("${name()} format headers end")log.info("${name()} format columns start")val mapper = ObjectMapper()val columns = mutableListOf<Any>()request.columns.forEach { column ->val jsonNode = mapper.createObjectNode()for (headerIndex in request.headers.indices){val header = request.headers[headerIndex] as Stringwhen (column){is List<*> -> jsonNode.putPOJO(header, column[headerIndex])else -> jsonNode.putPOJO(header, column)}}columns.add(jsonNode)}response.columns = columnslog.info("${name()} format columns end")log.info("${name()} format end time [ ${DateUtils.now()} ]")response.successful = true}catch (e: IOException){response.successful = falseresponse.message = e.message}return response}override fun formatStream(request: FileRequest): FileResponse{val response = FileResponse()try{requireNonNull("Stream must not be null")log.info("${name()} format stream start time [ ${DateUtils.now()} ]")val mapper = ObjectMapper()request.stream?.let {BufferedReader(InputStreamReader(it, Charsets.UTF_8)).use { reader ->val jsonNode: JsonNode = mapper.readTree(reader)log.info("${name()} format stream json node count [ ${jsonNode.size()} ]")val headers = mutableListOf<Any>()if (jsonNode.isArray && jsonNode.size() > 0){jsonNode[0].fieldNames().forEachRemaining { headers.add(it) }}response.headers = headersval columns = mutableListOf<Any>()if (jsonNode.isArray){jsonNode.elements().forEachRemaining { node ->val column = mutableMapOf<String, Any?>()node.fields().forEachRemaining { field ->column[field.key] = field.value}columns.add(column)}}response.columns = columnsit.close()}}log.info("${name()} format stream end time [ ${DateUtils.now()} ]")response.successful = true}catch (e: IOException){response.successful = falseresponse.message = e.message}return response}override fun writer(request: FileRequest): FileResponse{val response = FileResponse()try{log.info("${name()} writer origin path [ ${request.path} ]")log.info("${name()} writer start time [ ${DateUtils.now()} ]")val file = formatFile(request, name())log.info("${name()} writer file absolute path [ ${file.absolutePath} ]")val factory = JsonFactory()factory.createGenerator(file, JsonEncoding.UTF8).use { generator ->generator.writeStartArray()request.columns.forEach { column ->generator.writeStartObject()for (headerIndex in request.headers.indices){when (column){is List<*> -> generator.writeObjectField(request.headers[headerIndex] as String, column[headerIndex])is ObjectNode ->{generator.codec = ObjectMapper()val header = request.headers[headerIndex] as Stringgenerator.writeObjectField(header, column.get(header))}else -> generator.writeObjectField(request.headers[headerIndex] as String, column)}}generator.writeEndObject()}generator.writeEndArray()}log.info("${name()} writer end time [ ${DateUtils.now()} ]")response.path = file.absolutePathresponse.successful = true}catch (e: IOException){response.successful = falseresponse.message = e.message}catch (e: JsonGenerationException){response.successful = falseresponse.message = e.message}return response}override fun reader(request: FileRequest): FileResponse{val response = FileResponse()try{log.info("${name()} reader origin path [ ${request.path} ]")log.info("${name()} reader start time [ ${DateUtils.now()} ]")val file = formatFile(request, name())log.info("${name()} reader file absolute path [ ${file.absolutePath} ]")val mapper = ObjectMapper()val jsonNode: JsonNode = mapper.readTree(file)log.info("${name()} reader file json node count [ ${jsonNode.size()} ]")log.info("${name()} reader file headers start")val headers = mutableListOf<Any>()if (jsonNode.isArray && jsonNode.size() > 0){jsonNode[0].fieldNames().forEachRemaining { headers.add(it) }}response.headers = headerslog.info("${name()} reader file headers end")log.info("${name()} reader file columns start")val columns = mutableListOf<Any>()if (jsonNode.isArray){jsonNode.elements().forEachRemaining { node ->val column = mutableMapOf<String, Any?>()node.fields().forEachRemaining { field ->column[field.key] = field.value}columns.add(column)}}response.columns = columnslog.info("${name()} reader file columns end")response.successful = true}catch (e: Exception){response.successful = falseresponse.message = e.message}return response}
}

File SPI 加载器


resources 源目录下添加 META-INFservices 目录,格式为 resources/META-INF/services,创建 io.edurt.datacap.file.FileModule 文件,内容如下

io.edurt.datacap.file.json.JsonModule

通过以上内容我们实现了 Json 文件转换器的支持。我们只需要在要使用 Json 文件转换器的地方引用该模块即可。比如我们在 server 模块中使用到该模块,则在 server/pom.xml 文件中增加以下内容

<dependency><groupId>io.edurt.datacap</groupId><artifactId>datacap-file-json</artifactId><version>${project.version}</version>
</dependency>

Json Module 测试


package io.edurt.datacap.file.jsonimport com.google.inject.Guice.createInjector
import com.google.inject.Injector
import com.google.inject.Key
import com.google.inject.TypeLiteral
import io.edurt.datacap.file.File
import io.edurt.datacap.file.FileManager
import org.junit.Assert.assertEquals
import org.junit.Testclass JsonModuleTest
{private val injector: Injector = createInjector(FileManager())@Testfun test(){injector.getInstance(Key.get(object : TypeLiteral<Set<File>>(){})).stream().findFirst().ifPresent {assertEquals("Json", it.name())}}
}

Json SPI 测试


package io.edurt.datacap.file.jsonimport com.google.inject.Guice.createInjector
import com.google.inject.Injector
import io.edurt.datacap.file.FileFilter
import io.edurt.datacap.file.FileManager
import io.edurt.datacap.file.model.FileRequest
import org.junit.Before
import org.junit.Test
import org.slf4j.LoggerFactory.getLogger
import java.io.File
import java.io.FileInputStream
import kotlin.test.assertTrueclass JsonFileTest
{private val log = getLogger(this::class.java)private val name = "Json"private var injector: Injector? = nullprivate val request: FileRequest = FileRequest()@Beforefun before(){injector = createInjector(FileManager())request.name = "test"request.path = System.getProperty("user.dir")request.headers = listOf("name", "age")val l1 = listOf("Test", 12)val l2 = listOf("Test1", 121)request.columns = listOf(l1, l2)}@Testfun testFormat(){injector?.let { injector ->FileFilter.filter(injector, name).ifPresent { file ->val response = file.format(request)log.info("headers: [ ${response.headers} ]")response.columns.let { columns ->columns.forEachIndexed { index, line ->log.info("index: [ $index ], line: [ $line ]")}}assertTrue {response.successful == true}}}}@Testfun testFormatStream(){injector?.let { injector ->FileFilter.filter(injector, name).ifPresent { file ->request.stream = FileInputStream(File("${System.getProperty("user.dir")}/${request.name}.json"))val response = file.formatStream(request)log.info("headers: [ ${response.headers} ]")response.columns.let { columns ->columns.forEachIndexed { index, line ->log.info("index: [ $index ], line: [ $line ]")}}assertTrue {response.successful == true}}}}@Testfun testWriter(){injector?.let { injector ->FileFilter.filter(injector, name).ifPresent { file ->assertTrue {file.writer(request).successful == true}}}}@Testfun testReader(){injector?.let { injector ->FileFilter.filter(injector, name).ifPresent { file ->val response = file.reader(request)log.info("headers: ${response.headers}")response.columns.forEach {log.info("columns: $it")}assertTrue {response.successful == true}}}}
}

相关文章:

DataCap 自定义 File 转换器

DataCap 支持自定义 File 转换器&#xff0c;使用者可以编写自己的文件转换器集成到 DataCap 中。该文档主要讲解如何快速集成一个文件转换器到 DataCap 系统中。 该模块我们主要使用到的是 file 模块内的代码&#xff0c;我们本文使用 json 来做示例。 模块基本配置 新建项目…...

ARM32开发--IIC原理

知不足而奋进 望远山而前行 目录 文章目录 前言 目标 内容 I2C通讯规则 I2C写操作 I2C读流程 通讯信号 开始 结束 发送数据 bit发送 Byte发送 等待响应 接收数据 bit接收 Byte接收 发送响应 总结 前言 在现代消费电子和工业电子领域&#xff0c;各种类型的…...

列表、集合、字典的相关练习

1、使用列表推导式&#xff0c;输出1~100的所有素数 输入下面代码 # 定义一个辅助函数用来判断是否是素数 def is_prime(num):if num < 1:return Falsefor i in range(2, int(num**0.5) 1): #int函数将结果转换为整数&#xff0c;向下取整if num % i 0:return Falsere…...

填报志愿选大学专业,文科生如何选专业?

读文科的同学接触的专业知识相对广泛&#xff0c;往往被认为是“万金油”&#xff0c;他们仿佛什么都能做&#xff0c;但是和专业技能类知识不同&#xff0c;缺乏技术支持&#xff0c;从而使得文科专业的就业方向和前景远远比不上理科专业那么明朗&#xff0c;对于众多文科生而…...

如何实现跨域

如何实现跨域 当浏览器执行JS脚本时&#xff0c;会检测脚本要访问的协议&#xff0c;域名&#xff0c;端口号是不是和当前网址一致&#xff0c;不一致就是跨域。 跨域是不允许的&#xff0c;这种限制叫做浏览器的同源策略&#xff0c;简单就是浏览器不允许一个源加载脚本与其…...

从零开始利用树莓派+扬声器,实现简单的蓝牙音箱,手机连接放歌

背景 树莓派4B自带蓝牙和Wifi, 无需外接 USB dongle; 蓝牙最常见的应用是近距离传输数据,比如蓝牙传文件,蓝牙音箱等。正好家里有个普通的usb供电的便携音箱; 本文用树莓派蓝牙+普通音箱,实现简单的蓝牙音箱。 大致分为三个部分: kernel/driver层的ALSA驱动框架蓝牙音…...

今年的就业环境不容乐观,你想好怎么应对了吗

今年的就业环境不容乐观&#xff0c;你想好怎么应对了吗 毕业生进入职场的历程往往充满挑战和未知&#xff0c;尤其是在当前经济环境下&#xff0c;失业问题愈发凸显。本文通过分享几位年轻人的真实经历&#xff0c;剖析大学生及职场人士面临的困境&#xff0c;并提供应对策略…...

测试 halcon measure_projection 算子

期望结果完全相同&#xff0c;但是下面的测试结果和halcon的差值如下: [0.132838, 0.231991, 0.265157, 0.296903, 0.0998573, 0.165907, 0.230686, 0.130266, 0.0977104, 0.197109, 0.198173, 0.197086, 0.190943, 0.177665, 0.163521, 0.146541, 0.161362, 0.166666, 0.2281…...

网络安全岗位必须知道到:高性能抓取,多线程,异步逆向分析(Js逆向破解/APP逆向破解)反爬原理和解决方法,不然你的Python会有Bug

JS逆向破解和APP逆向破解以及反爬技术是网络安全和数据保护领域的重要话题。 遵循安全开发的最佳实践&#xff0c;包括输入验证、数据加密、权限管理等&#xff0c;以减少安全漏洞的出现。 坚决维护网络安全及开发安全&#xff0c;杜绝一切被爬&#xff0c;非法爬取数据的行为…...

lua网站开发中如何制作自定义模块

自定义模块是FastWeb框架的重要拓展功能&#xff0c;用来扩展和增强服务的能力。通过自定义模块&#xff0c;开发者可以轻松添加特定的功能和特性&#xff0c;使得网站开发更加灵活和高效。本文将演示如何添加自己的模块作为FastWeb的拓展&#xff0c;为框架的壮大与支持提供重…...

线性规划问题——单纯形算法

第一步&#xff1a;化“约束标准型” 在每个等式约束中至少有一个变量的系数为正&#xff0c;且这个变量只在该约束中出现。在每个约束方程中选择一个这样的变量称为基本变量。 剩下变量称为非基本变量。 一个简单的栗子 上图是一个约束标准型线性规划的例子。 等式1&#x…...

ADS基础教程20 - 电磁仿真(EM)参数化

EM介绍 一、引言二、参数化设置1.参数定义2.参数赋值3.创建EM模型和符号 四、总结 一、引言 参数化EM仿真&#xff0c;是在Layout环境下创建参数&#xff0c;相当于在原理图中声明变量。 二、参数化设置 1.参数定义 1&#xff09;在Layout视图&#xff0c;菜单栏中选中EM&g…...

NAND flash测试-雷龙发展

文章目录 一、简介 二、速度测试 最近比较忙&#xff0c;也一直没空发什么文章&#xff0c;这算是新年第一篇吧&#xff0c;正好最近收到了一个雷龙的flash芯片&#xff0c;先拿来玩一下吧。 有兴趣的小伙伴可以去雷龙官网找小姐姐领取一个免费试用。 一、简介 大概样子就是上面…...

CMake的学习之路

目录 一、基础命令 二、编译选项和设置 三、文件和目录操作 四、控制流命令 五、其他命令 六、CMake构建级别 CMake是一个跨平台的自动化建构系统&#xff0c;它使用一种人类可读的配置文件&#xff08;CMakeLists.txt&#xff09;来控制软件编译过程。以下是CMake中的一些…...

算法体系-22 第二十二节:暴力递归到动态规划(四)

一 最小距离累加和 1.1 描述 给定一个二维数组matrix&#xff0c;一个人必须从左上角出发&#xff0c;最后到达右下角 沿途只可以向下或者向右走&#xff0c;沿途的数字都累加就是距离累加和 返回最小距离累加和 1.2 分析...

Docker:利用Docker搭建一个nginx服务

文章目录 搭建一个nginx服务认识nginx服务Web服务器反向代理服务器高性能特点 安装nginx启动nginx停止nginx查找nginx镜像拉取nginx镜像&#xff0c;启动nginx站点其他方式拉取nginx镜像信息通过 DIGEST 拉取镜像 搭建一个nginx服务 首先先认识一下nginx服务&#xff1a; NGI…...

docker Pulling fs layer 含义

在使用Docker时&#xff0c;当你执行 docker pull 命令来获取一个新的镜像&#xff0c;控制台输出中可能会出现 "Pulling fs layer" 的信息。这是Docker拉取镜像过程中的一个步骤&#xff0c;下面是对这一过程的解释&#xff1a; Docker 镜像是由一系列的层&#xf…...

c#中上传超过30mb的文件,接口一直报404,小于30mb的却可以上传成功

在一次前端实现上传视频文件时,超过30mb的文件上传,访问接口一直报404,但是在Swagger中直接访问接口确是正常的,且在后端控制器中添加了限制特性,如下 但是却仍然报404,在apifox中请求接口也是报404, 网上说: 在ASP.NET Core中,配置请求过来的文件上传的大小限制通常…...

VRRP跟踪接口及认证(华为)

#交换设备 VRRP跟踪接口及认证 一、相关概念 1.VRRP跟踪接口 当 VRRP 的 Master 设备的上行接口出现问题, 而 Master 设备一直保持 Active 状态&#xff0c;那么就会导致网络出现中断&#xff0c;所以必须要使得 VRRP 的运行状态和上行接口能够关联。在配置了 VRRP 元余的网…...

梯度提升树GBDT系列算法

Boosting方法的基本元素与基本流程&#x1f4ab; 在Boosting集成算法当中&#xff0c;我们逐一建立多个弱评估器&#xff08;基本是决策树&#xff09;&#xff0c;并且下一个弱评估器的建立方式依赖于上一个弱评估器的评估结果&#xff0c;最终综合多个弱评估器的结果进行输出…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...