SpringBatch文件读写ItemWriter,ItemReader使用详解
SpringBatch文件读写ItemWriter,ItemReader使用详解
- 1. ItemReaders 和 ItemWriters
- 1.1. ItemReader
- 1.2. ItemWriter
- 1.3. ItemProcessor
- 2.FlatFileItemReader 和 FlatFileItemWriter
- 2.1.平面文件
- 2.1.1. FieldSet
- 2.2. FlatFileItemReader
- 2.3. FlatFileItemWriter
- 3.FlatFileItemReader 和 FlatFileItemWriter 使用案例
1. ItemReaders 和 ItemWriters
所有批处理都可以以其最简单的形式描述为读取大量数据,执行某种类型的计算或转换并写出结果。 Spring Batch 提供了三个关键接口来帮助执行批量读取和写入:ItemReader,ItemProcessor 和 ItemWriter。
1.1. ItemReader
注:内容来源于springbatch官网: https://docs.spring.io/spring-batch/reference/readers-and-writers/item-reader.html
尽管是一个简单的概念,但ItemReader是用于从许多不同类型的 Importing 中提供数据的方法。最一般的示例包括:
-
平面文件:平面文件 Item 读取器从平面文件中读取数据行,该文件通常描述记录,这些记录的数据字段由文件中的固定位置定义或由某些特殊字符(例如逗号)分隔。
-
XML:XML ItemReaders独立于用于解析,Map 和验证对象的技术来处理 XML。Importing 数据允许根据 XSD 模式验证 XML 文件。
-
数据库:访问数据库资源以返回结果集,该结果集可以 Map 到对象以进行处理。默认的 SQL ItemReader实现调用RowMapper来返回对象,如果需要重新启动,则跟踪当前行,存储基本统计信息,并提供一些事务增强功能,这些将在后面进行说明。
ItemReader是通用 Importing 操作的基本接口,如以下接口定义所示:
public interface ItemReader<T> {T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException;
}
read方法定义ItemReader的最基本 Contract。调用它会返回一个 Item;如果没有更多 Item,则返回null。一个 Item 可能代表文件中的一行,数据库中的一行或 XML 文件中的元素。通常希望将它们 Map 到可用的域对象(例如Trade,Foo或其他),但是 Contract 中没有要求这样做。
1.2. ItemWriter
ItemWriter在功能上与ItemReader类似,但是具有相反的运算。资源仍然需要定位,打开和关闭,但是它们的区别在于ItemWriter是写出而不是读入。对于数据库或队列,这些操作可能是插入,更新或发送。输出序列化的格式特定于每个批处理作业。
与ItemReader一样,ItemWriter是一个相当通用的接口,如以下接口定义所示:
public interface ItemWriter<T> {void write(List<? extends T> items) throws Exception;
}
与ItemReader上的read一样,write提供ItemWriter的基本 Contract。只要打开,它就会尝试写出传入 Item 的列表。因为通常期望将 Item“分批”在一起,然后输出,所以接口接受 Item 列表,而不是 Item 本身。写入列表后,可以执行任何必要的刷新操作,然后再从 write 方法返回。例如,如果写入一个 Hibernate DAO,则可以进行多个写入操作,每个 Item 一个。然后,Writer 可以在休眠会话上调用flush,然后再返回。
1.3. ItemProcessor
ItemReader和ItemWriter接口对于它们的特定任务都非常有用,但是如果要在编写之前插入业务逻辑怎么办?读写的一种选择是使用复合模式:创建包含另一个ItemWriter的ItemWriter或包含另一个ItemReader的ItemReader。以下代码显示了一个示例:
public class CompositeItemWriter<T> implements ItemWriter<T> {ItemWriter<T> itemWriter;public CompositeItemWriter(ItemWriter<T> itemWriter) {this.itemWriter = itemWriter;}public void write(List<? extends T> items) throws Exception {//Add business logic hereitemWriter.write(items);}public void setDelegate(ItemWriter<T> itemWriter){this.itemWriter = itemWriter;}
}
上一类包含另一个ItemWriter,它在提供了一些业务逻辑后将其委托给该ItemWriter。该模式也可以很容易地用于ItemReader,也许可以基于主ItemReader提供的 Importing 来获取更多参考数据。如果您需要自己控制对write的调用,它也很有用。但是,如果您只想在实际写入之前“转换”传递给写入的 Item,则无需自己write。您可以只修改 Item。对于这种情况,Spring Batch 提供了ItemProcessor接口,如以下接口定义所示:
public interface ItemProcessor<I, O> {O process(I item) throws Exception;
}
ItemProcessor很简单。给定一个对象,对其进行转换,然后返回另一个。提供的对象可以是或可以不是相同的类型。关键是可以在流程中应用业务逻辑,并且完全由开发人员来创建该逻辑。 ItemProcessor可以直接连接到步骤中。例如,假设ItemReader提供了Foo类型的类,并且在将其写出之前需要将其转换为Bar类型。以下示例显示了执行转换的ItemProcessor:
public class Foo {}public class Bar {public Bar(Foo foo) {}
}public class FooProcessor implements ItemProcessor<Foo,Bar>{public Bar process(Foo foo) throws Exception {//Perform simple transformation, convert a Foo to a Barreturn new Bar(foo);}
}public class BarWriter implements ItemWriter<Bar>{public void write(List<? extends Bar> bars) throws Exception {//write bars}
}
在前面的示例中,存在一个类Foo,一个类Bar和一个FooProcessor,它们坚持ItemProcessor接口。转换很简单,但是任何类型的转换都可以在这里完成。 BarWriter写入Bar对象,如果提供任何其他类型,则抛出异常。同样,如果提供了Foo以外的内容,则FooProcessor引发异常。然后可以将FooProcessor注入到Step中,如以下示例所示:
@Bean
public Job ioSampleJob() {return this.jobBuilderFactory.get("ioSampleJOb").start(step1()).end().build();
}@Bean
public Step step1() {return this.stepBuilderFactory.get("step1").<String, String>chunk(2).reader(fooReader()).processor(fooProcessor()).writer(barWriter()).build();
}
2.FlatFileItemReader 和 FlatFileItemWriter
注:内容来源springbatch官网链接: https://docs.spring.io/spring-batch/reference/readers-and-writers/flat-files/field-set.html
2.1.平面文件
交换批量数据的最常见机制之一一直是平面文件。与 XML 具有定义其结构化(XSD)的公认标准不同,任何阅读平面文件的人都必须提前了解文件的结构。通常,所有平面文件都分为两种:定界文件和定长文件。分隔文件是指用逗号分隔分隔符的字段。固定长度文件具有设置长度的字段。
2.1.1. FieldSet
在 Spring Batch 中使用平面文件时,无论是用于 Importing 还是输出,最重要的类之一是FieldSet。许多体系结构和库都包含用于帮助您从文件读入的抽象,但是它们通常返回String或String对象的数组。这真的只会让您半途而废。 FieldSet是 Spring Batch 的抽象,用于启用文件资源中字段的绑定。它使开发人员可以像处理数据库 Importing 一样使用文件 Importing。 FieldSet在概念上与 JDBC ResultSet类似。 FieldSet仅需要一个参数:String令牌数组。 (可选)您还可以配置字段的名称,以便可以按ResultSet之后的模式通过索引或名称来访问字段,如以下示例所示:
String[] tokens = new String[]{"foo", "1", "true"};
FieldSet fs = new DefaultFieldSet(tokens);
String name = fs.readString(0);
int value = fs.readInt(1);
boolean booleanValue = fs.readBoolean(2);
FieldSet界面上还有更多选项,例如Date,long,BigDecimal等等。 FieldSet的最大优点是它提供了平面文件 Importing 的一致解析。在处理由格式异常引起的错误或进行简单的数据转换时,它可以保持一致,而不是使每个批处理作业以潜在的意外方式进行不同的解析。
2.2. FlatFileItemReader
平面文件是最多包含二维(表格)数据的任何类型的文件。名为FlatFileItemReader的类有助于在 Spring Batch 框架中读取平面文件,该类提供了用于读取和解析平面文件的基本功能。 FlatFileItemReader的两个最重要的必需依存关系是Resource和LineMapper。 LineMapper接口将在下一部分中进行详细介绍。 resource 属性表示一个 Spring Core Resource。可以在Spring 框架资源中找到说明如何创建此类 bean 的文档。
Resource resource = new FileSystemResource("resources/trades.csv");
FlatFileItemReader中的其他属性使您可以进一步指定数据的解释方式,如下表所述:
LineMapper
与RowMapper一样,它采用诸如ResultSet之类的低级构造并返回Object,平面文件处理需要相同的构造才能将String行转换为Object,如以下接口定义所示:
public interface LineMapper<T> {T mapLine(String line, int lineNumber) throws Exception;
}
基本约定是,给定当前行及其关联的行号,Map 器应返回结果域对象。这类似于RowMapper,因为每一行都与其行号相关联,就像ResultSet中的每一行都与其行号绑定一样。这允许将行号绑定到结果域对象,以进行身份比较或提供更多信息。但是,与RowMapper不同,LineMapper被赋予了原始行,如上所述,该原始行只会使您到达中间。该行必须标记为FieldSet,然后可以将其 Map 到一个对象
LineTokenizer
必须将 Importing 行转换为FieldSet的抽象,因为可能需要将多种格式的平面文件数据转换为FieldSet。在 Spring Batch 中,此接口是LineTokenizer:
public interface LineTokenizer {FieldSet tokenize(String line);
}
LineTokenizer的约定是这样的:给定 Importing 行(理论上String可以包含多条线),则返回代表该行的FieldSet。然后可以将此FieldSet传递给FieldSetMapper。 Spring Batch 包含以下LineTokenizer实现:
-
DelimitedLineTokenizer:用于 Logging 的字段由定界符分隔的文件。最常见的定界符是逗号,但是也经常使用竖线或分号。
-
FixedLengthTokenizer:用于 Logging 的字段均为“固定宽度”的文件。必须为每种记录类型定义每个字段的宽度。
-
PatternMatchingCompositeLineTokenizer:通过检查模式,确定应在特定行上使用标记器列表中的哪个LineTokenizer。
- FieldSetMapper
FieldSetMapper接口定义单个方法mapFieldSet,该方法采用FieldSet对象并将其内容 Map 到对象。根据作业的需要,此对象可以是自定义 DTO,域对象或数组。 FieldSetMapper与LineTokenizer结合使用,可将一行数据从资源转换为所需类型的对象,如以下接口定义所示:
public interface FieldSetMapper<T> {T mapFieldSet(FieldSet fieldSet) throws BindException;
}
使用的模式与JdbcTemplate使用的RowMapper相同。
DefaultLineMapper
既然已经定义了读取平面文件的基本接口,那么很明显,需要三个基本步骤:
- 从文件中读取一行。
- 将String行传递到LineTokenizer#tokenize()方法中以检索FieldSet。
- 将标记化返回的FieldSet传递给FieldSetMapper,并从ItemReader#read()方法返回结果。
上述两个接口代表两个单独的任务:将线转换为FieldSet并将FieldSetMap 到域对象。因为LineTokenizer的 Importing 与LineMapper的 Importing(一条线)匹配,并且FieldSetMapper的输出与LineMapper的输出匹配,所以提供了同时使用LineTokenizer和FieldSetMapper的默认实现。下列类定义中显示的DefaultLineMapper表示大多数用户需要的行为:
public class DefaultLineMapper<T> implements LineMapper<>, InitializingBean {private LineTokenizer tokenizer;private FieldSetMapper<T> fieldSetMapper;public T mapLine(String line, int lineNumber) throws Exception {return fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));}public void setLineTokenizer(LineTokenizer tokenizer) {this.tokenizer = tokenizer;}public void setFieldSetMapper(FieldSetMapper<T> fieldSetMapper) {this.fieldSetMapper = fieldSetMapper;}
}
以上功能是默认实现中提供的,而不是内置于 Reader 本身中(如在框架的先前版本中所做的那样),以使用户在控制解析过程时具有更大的灵 Active,尤其是在需要访问原始行的情况下。
2.3. FlatFileItemWriter
写入平面文件具有相同的问题和必须从文件中读取的问题。步骤必须能够以事务方式编写定界或定长格式。
LineAggregator
就像需要LineTokenizer接口来获取一项并将其变成String一样,文件写入必须具有一种将多个字段聚合到单个字符串中以写入文件的方法。在 Spring Batch 中,这是LineAggregator,如以下接口定义所示:
public interface LineAggregator<T> {public String aggregate(T item);
}
LineAggregator与LineTokenizer逻辑相反。 LineTokenizer取String并返回FieldSet,而LineAggregator取item并返回String。
PassThroughLineAggregator
LineAggregator接口的最基本实现是PassThroughLineAggregator,它假定对象已经是一个字符串或它的字符串表示形式可以接受写入,如以下代码所示:
public class PassThroughLineAggregator<T> implements LineAggregator<T> {public String aggregate(T item) {return item.toString();}
}
如果需要直接控制创建字符串,但是FlatFileItemWriter的优点(例如事务和重新启动支持)是必需的,则上述实现很有用。
简化文件编写示例
既然已经定义了LineAggregator接口及其最基本的实现PassThroughLineAggregator,那么可以说明基本的编写流程:
- 要写入的对象被传递到LineAggregator以获得String。
- 返回的String将被写入配置的文件。
FlatFileItemWriter的以下摘录用代码表示:
public void write(T item) throws Exception {write(lineAggregator.aggregate(item) + LINE_SEPARATOR);
}
一个简单的配置可能如下所示:
@Bean
public FlatFileItemWriter itemWriter() {return new FlatFileItemWriterBuilder<Foo>().name("itemWriter").resource(new FileSystemResource("target/test-outputs/output.txt")).lineAggregator(new PassThroughLineAggregator<>()).build();
}
FieldExtractor
前面的示例对于写入文件的最基本用途可能很有用。但是,FlatFileItemWriter的大多数用户都有一个域对象,需要将该域对象写出,因此必须将其转换为一行。在读取文件时,需要满足以下条件:
- 从文件中读取一行。
- 将行传递到LineTokenizer#tokenize()方法中,以便检索FieldSet。
将标记化返回的FieldSet传递给FieldSetMapper,并从ItemReader#read()方法返回结果。
文件写入具有相似但相反的步骤:
- 将要写入的 Item 传递给 Writer。
- 将 Item 上的字段转换为数组。
- 将结果数组聚合为一行。
因为框架没有办法知道需要写出对象中的哪些字段,所以必须写一个FieldExtractor来完成将 Item 变成数组的任务,如以下接口定义所示:
public interface FieldExtractor<T> {Object[] extract(T item);
}
FieldExtractor接口的实现应从提供的对象的字段中创建一个数组,然后可以使用元素之间的分隔符将其写出,也可以将其写为固定宽度的行的一部分。
PassThroughFieldExtractor
在许多情况下,需要写出集合,例如数组Collection或FieldSet。从这些集合类型之一中“提取”数组非常简单。为此,请将集合转换为数组。因此,在这种情况下应使用PassThroughFieldExtractor。应当注意,如果传入的对象不是集合的类型,则PassThroughFieldExtractor返回仅包含要提取的 Item 的数组。
BeanWrapperFieldExtractor
与在文件读取部分中介绍的BeanWrapperFieldSetMapper一样,通常最好配置如何将域对象转换为对象数组,而不是自己编写转换。 BeanWrapperFieldExtractor提供了此功能,如以下示例所示:
BeanWrapperFieldExtractor<Name> extractor = new BeanWrapperFieldExtractor<Name>();
extractor.setNames(new String[] { "first", "last", "born" });String first = "Alan";
String last = "Turing";
int born = 1912;Name n = new Name(first, last, born);
Object[] values = extractor.extract(n);assertEquals(first, values[0]);
assertEquals(last, values[1]);
assertEquals(born, values[2]);
此提取器实现只有一个必需的属性:要 Map 的字段的名称。就像BeanWrapperFieldSetMapper需要字段名称将FieldSet上的字段 Map 到所提供对象上的 setter 一样,BeanWrapperFieldExtractor也需要名称 Map 到 getter 来创建对象数组。值得注意的是,名称的 Sequences 决定了数组中字段的 Sequences。
3.FlatFileItemReader 和 FlatFileItemWriter 使用案例
下一篇: FlatFileItemReader 和 FlatFileItemWriter 使用案例
相关文章:

SpringBatch文件读写ItemWriter,ItemReader使用详解
SpringBatch文件读写ItemWriter,ItemReader使用详解 1. ItemReaders 和 ItemWriters1.1. ItemReader1.2. ItemWriter1.3. ItemProcessor 2.FlatFileItemReader 和 FlatFileItemWriter2.1.平面文件2.1.1. FieldSet 2.2. FlatFileItemReader2.3. FlatFileItemWriter 3…...
如何评估AI模型:评估指标的分类、方法及案例解析
如何评估AI模型:评估指标的分类、方法及案例解析 引言第一部分:评估指标的分类第二部分:评估指标的数学基础第三部分:评估指标的选择与应用第四部分:评估指标的局限性第五部分:案例研究第六部分:…...

程序员学CFA——经济学(七)
经济学(七) 汇率外汇市场外汇市场的功能外汇市场的参与者卖方买方 汇率的计算汇率报价基础货币与计价货币直接报价与间接报价外汇报价习惯 名义汇率和实际汇率货币的升值与贬值交叉汇率计算即期汇率与远期汇率即期汇率与远期汇率的概念远期升水/贴水远期…...

imx335帧率改到10fps的方法
验证: imx335.c驱动默认的帧率是30fps,要将 IMX335 的帧率更改为 10fps,需要调整与帧率相关的参数。FPS(frames per second,每秒帧数)通常由 sensor 的曝光时间(exposure time)和垂直总时间(VTS,Vertical Total Size)共同决定。VTS 定义了 sensor 完成一帧图像采集…...

Large Language Model系列之二:Transformers和预训练语言模型
Large Language Model系列之二:Transformers和预训练语言模型 1 Transformer模型 Transformer模型是一种基于自注意力机制的深度学习模型,它最初由Vaswani等人在2017年的论文《Attention Is All You Need》中提出,主要用于机器翻译任务。随…...

java后端项目启动失败,解决端口被占用问题
报错信息: Web server failed to start . Port 8020 was already in use. 1、查看端口号 netstat -ano | findstr 端口号 2、终止进程 taskkill /F /PID 进程ID 举例:关闭8020端口...

PostgreSQL安装/卸载(CentOS、Windows)
说明:PostgreSQL与MySQL一样,是一款开源免费的数据库技术,官方口号:The World’s Most Advanced Open Source Relational Database.(世界上最先进的开源关系数据库),本文介绍如何在Windows、Cen…...
OutOfMemoryError异常OOM排查
目录 参考工具MAT(Memory Analyzer)一、产生原因二、测试堆溢出 java.lang.OutOfMemoryError: Java heap space测试代码运行手动导出dump文件mat排查打开dump文件查看Leak Suspects(泄露疑点)参考 【JVM】八、OOM异常的模拟 MAT工具分析Dump文件(大对象定位) 用arthas排…...
【Python】Arcpy将excel点生成shp文件
根据excel点经纬度数据,生成shp,参考博主的代码,进行了修改,在属性表中保留excel中的数据。 参考资料:http://t.csdnimg.cn/OleyT 注意修改以下两句中的数字。 latitude float(row[1]) longitude float(row[2])imp…...

torch之从.datasets.CIFAR10解压出训练与测试图片 (附带网盘链接)
前言 从官网上下载的是长这个样子的 想看图片,咋办咧,看下面代码 import torch import torchvision import numpy as np import os import cv2 batch_size 50transform_predict torchvision.transforms.Compose([torchvision.transforms.ToTensor(),…...

什么ISP?什么是IAP?
做单片机开发的工程师经常会听到两个词:ISP和IAP,但新手往往对这两个概念不是很清楚,今天就来和大家聊聊什么是ISP,什么是IAP? 一、ISP ISP的全称是:In System Programming,即在系统编程&…...

外卖霸王餐系统怎么快速盈利赚钱?
微客云外卖霸王餐系统,作为近年来外卖行业中的一股新兴力量,以其独特的商业模式和营销策略,迅速吸引了大量消费者的目光。该系统通过提供显著的折扣和返利,让顾客能够以极低的价格甚至免费享受到美味的外卖,同时&#…...
Linux环境下安装Nodejs
Linux环境下安装Nodejs 下载地址:https://nodejs.org/zh-cn/download/package-manager 一、使用压缩包自定义安装 上述链接下载好对应版本的软件包后,我存放到 /evn/nodejs 目录下(根据自己实际情况设置) 设置软链接 sudo ln…...
【Rust】字符串String类型学习
什么是String Rust的核心语言中只有一个String类型,那就是String slice,str通常被当作是&str的借用。String类型是通过标准库提供的,而不是直接编码到核心语言中,它是一个可增长的、可变的、utf-8编码的类型。str和String都是utf-8编码的…...

先验概率 后验概率 最大似然估计 自编码器AE
先验概率 先验概率:由因求果中的因 作用:后验概率是比较难以计算的,我们通常使用贝叶斯公式由先验概率计算后验概率。 贝叶斯公式:P(B|A)P(A|B)P(B)/P(A),其中P(B|A)为后验概率,P(A|B)为先验概率。 后验…...
qt 鼠标接近某线时,形状变化举例
1.qt 鼠标接近某线时,形状变化举例 在Qt中,要实现鼠标接近某条线时形状发生变化的效果,你需要利用QWidget的enterEvent和leaveEvent,或者更通用的mouseMoveEvent来检测鼠标的位置,并相应地改变鼠标的光标形状。 以下…...

800块,我从淘宝上买AGV……
导语 大家好,我是社长,老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》人俱乐部 从淘宝上打算够购买一台AGV小车,上去一搜,嘿,你别说,还真有。便宜的才200块钱。 很兴奋把…...

C++相关概念和易错语法(21)(虚函数、协变、析构函数的重写)
多态的核心是虚函数,本文从虚函数出发,根据原理慢慢推进得到结论,进而理解多态 1.虚函数 先看一下下面的代码,想想什么导致了这个结果 #include <iostream> using namespace std;class A { public:virtual void test(){co…...

SoulApp创始人张璐团队以AI驱动社交进化,平台社交玩法大变革
在科技飞速发展的今天,人工智能正逐步渗透到社交媒体的各个环节,赋能全链路社交体验。AI的引入不仅提升了内容推荐的精准度,使用户能够更快速地发现感兴趣的内容,还能通过用户行为预测,帮助平台更好地理解和满足用户需求。此外,AI驱动的虚拟助手和聊天机器人也正在改变用户互动…...

MySQL事务隔离级别+共享锁,排他锁,乐观锁,悲观锁
在操作数据库的时候,可能会由于并发问题而引起的数据的不一致性(数据冲突)。 MySQL事务隔离级别 一个事务的执行,本质上就是一条工作线程在执行,当出现多个事务同时执行时,这种情况则被称之为并发事务&am…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...