当前位置: 首页 > 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;最终综合多个弱评估器的结果进行输出…...

ARP 协议超详细讲解

前言网络设备有数据要发送给另一台网络设备时&#xff0c;必须要知道对方的网络层地址&#xff08;即IP地址&#xff09;。IP地址由网络层来提供&#xff0c;但是仅有IP地址是不够的&#xff0c;IP数据报文必须封装成帧才能通过数据链路进行发送。数据帧必须要包含目的MAC地址&…...

Phi-3-mini-4k-instruct-gguf实战案例:用轻量模型替代Llama3-8B做高频短任务降本

Phi-3-mini-4k-instruct-gguf实战案例&#xff1a;用轻量模型替代Llama3-8B做高频短任务降本 1. 为什么选择轻量模型 在AI应用落地的过程中&#xff0c;我们常常面临一个困境&#xff1a;大模型效果虽好&#xff0c;但部署成本高、响应速度慢。特别是在处理大量高频短任务时&…...

掌握5个核心配置技巧:OpenCore-Configurator从入门到专家

掌握5个核心配置技巧&#xff1a;OpenCore-Configurator从入门到专家 【免费下载链接】OpenCore-Configurator A configurator for the OpenCore Bootloader 项目地址: https://gitcode.com/gh_mirrors/op/OpenCore-Configurator OpenCore-Configurator&#xff08;简称…...

Qwen2.5-Coder-1.5B应用案例:自动生成Bash脚本处理日志文件

Qwen2.5-Coder-1.5B应用案例&#xff1a;自动生成Bash脚本处理日志文件 1. 日志处理场景与痛点分析 1.1 运维工程师的日常挑战 在服务器运维工作中&#xff0c;日志分析是最常见也最耗时的任务之一。想象一下这样的场景&#xff1a; 你需要检查10台服务器上50个不同的服务日…...

Excel也能搞定GRR!不用买昂贵软件,这份保姆级模板和计算指南请收好

Excel也能搞定GRR&#xff01;不用买昂贵软件&#xff0c;这份保姆级模板和计算指南请收好 在制造业质量管理中&#xff0c;测量系统分析&#xff08;MSA&#xff09;是确保数据可靠性的基石。但现实情况是&#xff0c;许多中小企业和初创团队面对动辄上万元的专业统计软件只能…...

华为HMS Scan Kit Customized View Mode:打造品牌专属扫码界面的实战指南

1. 为什么选择Customized View Mode&#xff1f; 扫码功能已经成为现代App的标配&#xff0c;但很多开发者面临一个两难选择&#xff1a;要么用系统默认的扫码界面显得千篇一律&#xff0c;要么完全自己开发一套又耗时耗力。华为HMS Scan Kit的Customized View Mode正好解决了这…...

08-多平台集成实战

OpenClaw 多平台集成实战 “让 AI 助手跨越每个通讯渠道,无处不在。” — OpenClaw 在当今多元化的通讯环境中,一个优秀的 AI 助手不应该被限制在单一平台上。OpenClaw 的核心优势之一就是其强大的多平台集成能力,能够同时连接 Discord、Telegram、飞书、企业微信、QQ、钉钉…...

思源宋体TTF:开源字体选型与商业价值指南

思源宋体TTF&#xff1a;开源字体选型与商业价值指南 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 你是否曾为商业项目的字体授权成本而困扰&#xff1f;是否在寻找既能满足专业设计…...

cool-admin(midway版)前端表单验证:AsyncValidator与异步校验完整指南

cool-admin(midway版)前端表单验证&#xff1a;AsyncValidator与异步校验完整指南 【免费下载链接】cool-admin-midway &#x1f525; cool-admin(midway版)一个很酷的后台权限管理框架&#xff0c;模块化、插件化、CRUD极速开发&#xff0c;永久开源免费&#xff0c;基于midwa…...

UI设计入门指南——Figma新手必备操作全解析

1. Figma入门&#xff1a;从零到第一个设计稿 第一次打开Figma时&#xff0c;很多人会被满屏的英文界面和复杂工具栏吓到。其实我刚接触时也一样&#xff0c;但现在回头看&#xff0c;掌握基础操作只需要30分钟。Figma作为目前最流行的UI设计工具&#xff0c;最大的优势就是零门…...