ExcelReader Controller Service
描述
解析 Microsoft Excel 文档,将每个工作表中的每一行作为单独的记录返回。 该阅读器允许从所有必需的工作表中推断模式或提供用于解释值的显式模式。有关更多文档,请参阅Controller Service的用法。 该阅读器能够处理受密码和非密码保护的现代 XLSX 以及较旧的 XLS Excel 电子表格。
标签
属性
| 名称 | 默认值 | 可选值 | 描述 |
|---|---|---|---|
| Schema Access Strategy | Use Starting Row |
| 指定如何获取用于解释数据的架构。 |
| Schema Registry | | 控制器服务 API:SchemaRegistrycn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8 | 指定用于架构Registry的Controller Service |
| Schema Name | ${schema.name} | 指定要在架构Registry属性中查找的架构名称 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Version | | 指定在 Schema Registry 中查找 Schema 的版本。如果未指定,则检索 Schema 的最新版本。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Branch | | 指定在 Schema Registry 属性中查找 Schema 时使用的分支名称。如果选择的 Schema Registry 不支持分支,则此值将被忽略。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Text | ${avro.schema} | Avro 格式的架构文本 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Schema Reference Reader | | 控制器服务 API:SchemaReferenceReadercn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8 | 服务实现负责读取 FlowFile 属性或内容以确定架构参考标识符 |
| Input File Type | XLSX |
| 指定 Excel 输入文件的类型。 |
| Protection Type | UNPROTECTED |
| 指定 Excel 电子表格是否受密码保护。 |
| Password | | 受密码保护的 Excel 电子表格的密码 敏感属性:是 | |
| 起始行 | 1 | 开始处理的第一行的行号(基于 1)。用于跳过工作表顶部不属于数据集的行。使用 'Use Starting Row' 策略时,此处应为列标题行。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Row Evaluation Strategy | STANDARD |
| 选择起始行之后有多少行用于确定架构的策略。 |
| Required Sheets | | 逗号分隔的 Excel 文档工作表名称列表,应从中提取行。如果此属性留空,则将从所有工作表中提取所有行。名称列表区分大小写。未在此值中指定的任何工作表将被忽略。如果指定的工作表未找到,将抛出异常。 支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值) | |
| Date Format | | 指定读取/写入日期字段时使用的格式。如果未指定,日期字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,MM/dd/yyyy 表示两位数字的月份,后跟两位数字的日期,后跟四位数字的年份,全部以 '/' 字符分隔,如 01/01/2017)。 | |
| Time Format | | 指定读取/写入时间字段时使用的格式。如果未指定,时间字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,HH:mm:ss 表示 24 小时格式的两位数字的小时,后跟两位数字的分钟,后跟两位数字的秒,全部以 ':' 字符分隔,如 18:04:15)。 | |
| Timestamp Format | | 指定读取/写入时间戳字段时使用的格式。如果未指定,时间戳字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,MM/dd/yyyy HH:mm:ss 表示两位数字的月份,后跟两位数字的日期,后跟四位数字的年份,全部以 '/' 字符分隔;然后后跟 24 小时格式的两位数字的小时,后跟两位数字的分钟,后跟两位数字的秒,全部以 ':' 字符分隔,如 01/01/2017 18:04:15)。 |
状态管理
该组件不存储状态。
受限 (Restricted)
该组件不受限。
提供的 Service API
RecordReaderFactory
详细说明
ExcelReader
ExcelReader 允许把输入数据解释为带分隔符的 Record。Excel 电子表格中的每一行是一条记录,每个单元格被视为一个字段。 该读取器允许选择从哪一行开始,以及摄取电子表格中的哪些工作表。 使用 "Use Starting Row" 策略时,字段名会取自所配置起始行的列名。如果 起始行有任何空白列,会自动为其分配以 "column_" 为前缀、以单元格编号命名的字段名。使用 "Infer Schema" 策略时,字段名会取自每列的单元格编号,并以 "column_" 为前缀。其他情况下,可以通过 Schema Text 指定 schema 或在 Schema Registry 中查找 schema 来提供字段名。
Schema 与类型强制转换
从传入数据解析记录时,记录会被拆分为字段。每个字段会按字段名 在配置的 schema 中查找,以确定数据的类型。如果字段不在 schema 中,该字段会从 Record 中省略。如果字段在 schema 中,接收到的数据类型会与 schema 指定的数据类型比较。如果类型匹配,字段值直接使用。如果 schema 表明字段应为不同类型,该 Controller Service 会尝试把数据强制转换为 schema 指定的类型。如果字段无法强制转换为指定类型,会抛出异常。
尝试把字段值从一种数据类型强制转换为另一种时,适用以下规则:
- Excel 把所有数值类型存储为 Double,它可以强制转换为其他任何数值数据类型(Byte、Short、Int、Long、Float)。
- 任何数据类型都可以强制转换为 String 类型。请注意,由于 Excel 把所有数字存储为 Double,把大数字强制转换为 String 会得到科学计数法的字符串表示。如果不希望这样,可以把数字强制转换为 Long 数值类型。
- 任何数值都可以强制转换为 Date、Time 或 Timestamp 类型,前提是把 Long 值视为自纪元(1970 年 1 月 1 日 GMT 午夜)以来的毫秒数。
- 如果 String 值的格式匹配配置的 "Date Format"、"Time Format" 或 "Timestamp Format",它可以强制转换为 Date、Time 或 Timestamp 类型。
- 如果值是合适的类型,String
值可以强制转换为数值。例如,String
值
8可以强制转换为任何数值类型。但 String 值8.2可以强制转换为 Double 或 Float 类型,不能转换为 Integer。 - "true" 或 "false"(不区分大小写)的 String 值可以强制转换为 Boolean 值。
- 非空的 String 值可以强制转换为 Char 类型。如果 String 包含多个字符,使用第一个字符,其余字符被忽略。
- 任何"日期/时间"类型(Date、Time、Timestamp)都可以强制转换为 其他任何"日期/时间"类型。
- 任何"日期/时间"类型都可以强制转换为 Long 类型,表示自纪元(1970 年 1 月 1 日 GMT 午夜)以来的毫秒数。
- 任何"日期/时间"类型都可以强制转换为 String。String 的格式是对应属性(Date Format、Time Format、 Timestamp Format 属性)所配置的 DateFormat。
尝试把值从一种数据类型强制转换为另一种时,如果上述规则都不适用, 强制转换会失败并抛出异常。
使用起始行与
Schema 推断
虽然 Lynxus 的 Record API 要求每条记录都有 schema,但根据数据中的值推断 schema 通常比手动创建 schema 更方便。这通过把 "Schema Access Strategy" 属性设为 "Use Starting Row" 或 "Infer Schema" 实现。使用 "Use Starting Row" 策略时,Reader 会解析 FlowFile 数据中所配置起始行之后的前十行来确定 schema,同时跟踪遇到的所有字段及每个字段的类型。然后形成一个 涵盖所有遇到字段的 schema。即使这十行中存在空白行也能推断出 schema,但如果它们全是空白行,该策略将无法创建 schema。使用 "Infer Schema" 策略时,Reader 会先解析 FlowFile 中的所有数据,跟踪遇到的所有字段及每个字段的类型, 以此确定 schema。所有数据解析完成后,形成一个涵盖所有遇到字段的 schema。
推断 schema 时的一个常见疑虑是如何处理同一字段两个值类型不同的情况。 例如,考虑包含以下两条记录的 FlowFile:
name, age
John, 8
Jane, Ten
显然 "name" 字段会被推断为 STRING 类型。但 "age" 字段该怎么处理?该字段应该是 INT 和 STRING 之间的 CHOICE 吗?应该优先 LONG 还是 INT?直接用 STRING 吗?该字段应该可为 null 吗?
为帮助理解该 Record Reader 如何推断 schema,以下是推断逻辑遵循的规则列表:
- 所有字段都被推断为可为 null。
- 当两条不同记录中同一字段遇到两个值(或 ARRAY 类型遇到两个值)时,推断引擎倾向于使用"更宽"的数据类型, 而不是 CHOICE 数据类型。当且仅当数据类型 "A" 包含数据类型 "B" 的所有值以及其他值时,称数据类型 "A" 比 "B" 宽。例如,LONG 类型比 INT 类型宽,但不比 BOOLEAN 类型宽(BOOLEAN 也不比 LONG 宽)。INT 比 SHORT 宽。STRING 类型被认为比除 MAP、RECORD、ARRAY 和 CHOICE 之外的所有类型都宽。
- 在推断值的类型之前,会移除首尾空白。此外,如果值被双引号
(") 包围,双引号会被移除。因此,值
16与"16"的解释相同,两者都会被解释为 INT。但值" 16"会被推断为 STRING 类型,因为空白被包含在双引号内,意味着空白被视为值的一部分。 - 如果配置了 "Time Format"、"Timestamp Format" 或 "Date Format" 属性,任何原本会被视为 STRING 类型的值都会先与配置的格式比对,看是否匹配其中任何一个。 如果值匹配 Timestamp Format,该值被视为 Timestamp 字段。如果匹配 Date Format,被视为 Date 字段。如果匹配 Time Format,被视为 Time 字段。在不太可能的情况下,值匹配多个配置的格式时, 匹配顺序为:Timestamp、Date、Time。也就是说,如果一个值同时匹配 Timestamp Format 和 Date Format,推断出的类型是 Timestamp。由于解析日期和时间可能开销较大,如果不期望出现 日期、时间和时间戳,或把数据处理为 STRING 也可接受,建议不要配置这些格式。不过,对于这很重要的 用例,推断引擎足够智能,会先检查几个开销很小的条件来优化解析。 例如,检查字符串长度是否过长或过短而无法匹配模式。这比尝试把 每个字符串值解析为时间戳要高效得多。
- 从不推断 MAP 类型。
- 从不推断 ARRAY 类型。
- 从不推断 RECORD 类型。
- 如果字段存在但所有值都为 null,该字段被推断为 STRING 类型。
推断
Schema 的缓存
该 Record Reader 要求:如果要推断 schema,必须读取所有记录,以确保推断出的 schema 适用于 FlowFile 中的所有记录。但这可能开销较大,尤其是数据要经过许多 不同转换时。为减轻推断 schema 的开销,可以为 Record Reader 配置 "Schema Inference Cache"(填写同名属性)。这是一个可以被 Record Reader 和 Record Writer 共享的 Controller Service。
每当使用 Record Writer 写入数据时,如果配置了 "Schema Cache",它也会把 schema 添加到 Schema Cache。这会把该 schema 的标识符作为 Attribute 添加到 FlowFile。
每当使用 Record Reader 读取数据时,如果配置了 "Schema Inference Cache",它会先查找 FlowFile 上的 "schema.cache.identifier" Attribute。如果该 Attribute 存在,它会用该 Attribute 的值在 schema 缓存中查找 schema。如果能在缓存中找到该标识符对应的 schema,就直接使用该 schema,而不是读取、解析和分析数据来推断 schema。如果 FlowFile 上没有该 Attribute,或该 Attribute 存在但缓存中没有该标识符对应的 schema,Record Reader 就会按上述方式推断 schema。
最终效果是,用户可以串联许多不同的 Processor 来处理面向记录的数据。通常,只有链中的第一个 Processor 会承担推断 schema 的"代价"。对于链中的所有其他 Processor,Record Reader 只需按标识符在 Schema Cache 中查找 schema 即可。这让 Record Reader 能准确推断 schema(因为它基于 FlowFile 中的所有数据推断),同时仍然高效,因为无论有多少 Processor 处理数据,schema 通常只推断一次。
示例
示例
1
例如,考虑一个内容为 Excel 电子表格的 FlowFile,其唯一的工作表包含以下内容:
id, name, balance, join_date, notes
1, John, 48.23, 04/03/2007, "Our very first customer!"
2, Jane, 1245.89, 08/22/2009,
3, Frank Franklin, "48481.29", 04/04/2016,
另外,假设该 Controller Service 配置为跳过第一行,并配置了指向 AvroSchemaRegistry 的 Schema Registry,其中包含以下 schema:
{
"namespace": "lynxus",
"name": "balances",
"type": "record",
"fields": [
{
"name": "id",
"type": "int"
},
{
"name": "name",
"type": "string"
},
{
"name": "balance",
"type": "double"
},
{
"name": "join_date",
"type": {
"type": "int",
"logicalType": "date"
}
},
{
"name": "notes",
"type": "string"
}
]
}
在上面的示例中,'join_date' 列是
Date 类型。为了让
Excel Reader 能正确把值解析为日期,需要给读取器提供要使用的日期格式。
本例中,我们会把
Date Format 属性配置为
MM/dd/yyyy,表示两位月份,后跟两位日期,再跟四位年份——各部分以
斜杠分隔。此时,结果是该
FlowFile 由
3 条不同的记录组成。第一条记录包含以下值:
| 字段名 | 字段值 |
|---|---|
| id | 1 |
| name | John |
| balance | 48.23 |
| join_date | 04/03/2007 |
| notes | Our very first customer! |
第二条记录包含以下值:
| 字段名 | 字段值 |
|---|---|
| id | 2 |
| name | Jane |
| balance | 1245.89 |
| join_date | 08/22/2009 |
| notes |
第三条记录包含以下值:
| 字段名 | 字段值 |
|---|---|
| id | 3 |
| name | Frank Franklin |
| balance | 48481.29 |
| join_date | 04/04/2016 |