-
Notifications
You must be signed in to change notification settings - Fork 232
优化Excel读取内存占用 #409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
CodeCasterX
merged 9 commits into
ModelEngine-Group:1.2.x
from
jsbjfkbsjk:excel-fast-optimization
Sep 16, 2025
Merged
优化Excel读取内存占用 #409
Changes from 1 commit
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
381b95c
把提取分析excel文件的功能做了插件化处理
jsbjfkbsjk 2514394
更正注释,格式和目录
jsbjfkbsjk 3384e17
更正格式
jsbjfkbsjk 4a2dc21
增加序列化能力和修改格式
jsbjfkbsjk 8ec5b8a
修改格式和健壮性处理
jsbjfkbsjk 66a9412
Update FileExtractor.java
jsbjfkbsjk 5aea926
修改格式
jsbjfkbsjk ea923af
Merge branch 'excel-fast-optimization' of https://github.com/jsbjfkbs…
jsbjfkbsjk 2bc9807
Update FileExtractorContainer.java
jsbjfkbsjk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>modelengine.fit.jade</groupId> | ||
| <artifactId>app-builder-plugin-parent</artifactId> | ||
| <version>1.0.0-SNAPSHOT</version> | ||
| </parent> | ||
|
|
||
| <artifactId>aipp-file-extract-excel</artifactId> | ||
|
|
||
| <dependencies> | ||
| <!-- FIT --> | ||
| <dependency> | ||
| <groupId>org.fitframework</groupId> | ||
| <artifactId>fit-api</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.fitframework</groupId> | ||
| <artifactId>fit-util</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>cn.idev.excel</groupId> | ||
| <artifactId>fastexcel</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>modelengine.fit.jade</groupId> | ||
| <artifactId>aipp-file-extract-service</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.fitframework</groupId> | ||
| <artifactId>fit-test-framework</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.assertj</groupId> | ||
| <artifactId>assertj-core</artifactId> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.fitframework</groupId> | ||
| <artifactId>fit-build-maven-plugin</artifactId> | ||
| <version>${fit.version}</version> | ||
| <executions> | ||
| <execution> | ||
| <id>build-plugin</id> | ||
| <goals> | ||
| <goal>build-plugin</goal> | ||
| </goals> | ||
| </execution> | ||
| <execution> | ||
| <id>package-plugin</id> | ||
| <goals> | ||
| <goal>package-plugin</goal> | ||
| </goals> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-antrun-plugin</artifactId> | ||
| <version>${maven.antrun.version}</version> | ||
| <executions> | ||
| <execution> | ||
| <phase>install</phase> | ||
| <configuration> | ||
| <target> | ||
| <copy file="${project.build.directory}/${project.build.finalName}.jar" | ||
| todir="../../../build/plugins"/> | ||
| </target> | ||
| </configuration> | ||
| <goals> | ||
| <goal>run</goal> | ||
| </goals> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
|
|
||
| </project> | ||
142 changes: 142 additions & 0 deletions
142
...xtract-excel/src/main/java/modelengine/fit/jade/aipp/file/extract/ExcelFileExtractor.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. | ||
| * This file is a part of the ModelEngine Project. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| package modelengine.fit.jade.aipp.file.extract; | ||
|
|
||
| import cn.idev.excel.ExcelReader; | ||
| import cn.idev.excel.FastExcel; | ||
| import cn.idev.excel.context.AnalysisContext; | ||
| import cn.idev.excel.converters.Converter; | ||
| import cn.idev.excel.enums.CellDataTypeEnum; | ||
| import cn.idev.excel.metadata.GlobalConfiguration; | ||
| import cn.idev.excel.metadata.data.DataFormatData; | ||
| import cn.idev.excel.metadata.data.ReadCellData; | ||
| import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| import cn.idev.excel.read.listener.ReadListener; | ||
| import cn.idev.excel.read.metadata.ReadSheet; | ||
| import cn.idev.excel.util.DateUtils; | ||
| import modelengine.fitframework.annotation.Component; | ||
| import modelengine.fitframework.annotation.Fitable; | ||
|
|
||
| import java.io.BufferedInputStream; | ||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.math.BigDecimal; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Paths; | ||
| import java.text.SimpleDateFormat; | ||
| import java.util.Date; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| @Component | ||
| public class ExcelFileExtractor implements AbstractFileExtractor { | ||
|
|
||
| private static String getCellValueAsString(ReadCellData<?> cell) { | ||
|
CodeCasterX marked this conversation as resolved.
Outdated
|
||
| switch (cell.getType()) { | ||
| case STRING: | ||
| return cell.getStringValue(); | ||
| case NUMBER: | ||
| DataFormatData fmt = cell.getDataFormatData(); | ||
| short formatIndex = fmt.getIndex(); | ||
| String formatString = fmt.getFormat(); | ||
| if (DateUtils.isADateFormat(formatIndex, formatString)) { | ||
| double value = cell.getNumberValue().doubleValue(); | ||
| Date date = DateUtils.getJavaDate(value, true); | ||
| return new SimpleDateFormat("yyyy-MM-dd").format(date); | ||
| } else { | ||
| BigDecimal num = cell.getNumberValue(); | ||
| return num.stripTrailingZeros().toPlainString(); | ||
| } | ||
| case BOOLEAN: | ||
| return Boolean.toString(cell.getBooleanValue()); | ||
| default: | ||
| return ""; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| @Fitable(id = "get-fileType-excel") | ||
| public FileTypeConstant.FileType supportedFileType() { | ||
| return FileTypeConstant.FileType.EXCEL; | ||
| } | ||
|
|
||
| /** | ||
| * 从指定路径的 Excel 文件中提取内容,并返回为字符串形式。 | ||
| * 实现方式: | ||
| * 基于 fast-excel 包,使用流式读取(ReadListener)逐行解析,避免一次性加载整表造成的内存开销。 | ||
| * 每行数据会被转换为以制表符(\t)分隔的文本,并在行末追加换行符。 | ||
| * 支持多 sheet 解析,会依次读取工作簿中的每一个 sheet。 | ||
| * | ||
| * @param fileUrl 表示文件路径的 {@link String}. | ||
| * @return 表示文件内容的 {@link String}。 | ||
| * @throws RuntimeException 当文件读取或解析失败时抛出 | ||
| */ | ||
| @Override | ||
| @Fitable(id = "extract-file-excel") | ||
| public String extractFile(String fileUrl) { | ||
|
CodeCasterX marked this conversation as resolved.
|
||
| File file = Paths.get(fileUrl).toFile(); | ||
| StringBuilder excelContent = new StringBuilder(); | ||
| ReadListener<Map<Integer, String>> listener = new ReadListener<>() { | ||
|
CodeCasterX marked this conversation as resolved.
Outdated
|
||
| @Override | ||
| public void invoke(Map<Integer, String> data, AnalysisContext context) { | ||
| String line = data.entrySet() | ||
| .stream() | ||
| .sorted(Map.Entry.comparingByKey()) | ||
| .map(e -> e.getValue() == null ? "" : e.getValue()) | ||
| .collect(Collectors.joining("\t")); | ||
| excelContent.append(line).append('\n'); | ||
| } | ||
|
|
||
| @Override | ||
| public void doAfterAllAnalysed(AnalysisContext context) { | ||
| } | ||
| }; | ||
| try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) { | ||
| ExcelReader reader = FastExcel.read(is, listener) | ||
| .registerConverter(new CustomCellStringConverter()) | ||
| .headRowNumber(0) | ||
| .build(); | ||
|
|
||
| List<ReadSheet> sheets = reader.excelExecutor().sheetList(); | ||
| for (ReadSheet meta : sheets) { | ||
| excelContent.append("Sheet ").append(meta.getSheetNo() + 1).append(':').append('\n'); | ||
| ReadSheet readSheet = FastExcel.readSheet(meta.getSheetNo()).headRowNumber(0).build(); | ||
| reader.read(readSheet); | ||
| } | ||
| excelContent.append('\n'); | ||
| reader.finish(); // 关闭资源 | ||
|
CodeCasterX marked this conversation as resolved.
Outdated
|
||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| return excelContent.toString(); | ||
| } | ||
|
|
||
| /** | ||
| * 自定义单元格数据转换器。 | ||
| * 将 Excel 单元格数据统一转换为字符串,避免数值/日期等类型在读取时格式不一致的问题。 | ||
| * 缺点:由于采用fast excel包,没有 FORMULA类,会将公式单元格自动计算为值 | ||
| */ | ||
| public static class CustomCellStringConverter implements Converter<String> { | ||
| @Override | ||
| public Class<String> supportJavaTypeKey() { | ||
| return String.class; | ||
| } | ||
|
|
||
| @Override | ||
| public CellDataTypeEnum supportExcelTypeKey() { | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, | ||
| GlobalConfiguration globalConfiguration) { | ||
| return getCellValueAsString(cellData); | ||
| } | ||
| } | ||
| } | ||
4 changes: 4 additions & 0 deletions
4
app-builder/plugins/aipp-file-extract-excel/src/main/resources/application.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| fit: | ||
| beans: | ||
| packages: | ||
| - 'modelengine.fit.jade.aipp.file.extract' |
39 changes: 39 additions & 0 deletions
39
...ct-excel/src/test/java/modelengine/fit/jade/aipp/file/extract/ExcelFileExtractorTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. | ||
| * This file is a part of the ModelEngine Project. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| package modelengine.fit.jade.aipp.file.extract; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| import modelengine.fitframework.annotation.Fit; | ||
| import modelengine.fitframework.test.annotation.FitTestWithJunit; | ||
|
|
||
| import org.junit.jupiter.api.Disabled; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.io.File; | ||
|
|
||
| @FitTestWithJunit(includeClasses = ExcelFileExtractor.class) | ||
| @Disabled | ||
|
CodeCasterX marked this conversation as resolved.
Outdated
|
||
| class ExcelFileExtractorTest { | ||
| @Fit | ||
| ExcelFileExtractor excelFileExtractor; | ||
|
|
||
| @Test | ||
| @DisplayName("测试获取支持文件类型") | ||
| void supportedFileType() { | ||
| assertThat(this.excelFileExtractor.supportedFileType()).isEqualTo(FileTypeConstant.FileType.EXCEL); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("测试 excel 文件提取成功") | ||
| void extractFile() { | ||
| File file = new File(this.getClass().getClassLoader().getResource("file/content.xlsx").getFile()); | ||
| assertThat(this.excelFileExtractor.extractFile(file.getAbsolutePath())).isEqualTo( | ||
| "Sheet 1:\nThis is an excel test\n\n"); | ||
| } | ||
| } | ||
Binary file added
BIN
+9.21 KB
app-builder/plugins/aipp-file-extract-excel/src/test/resources/file/content.xlsx
|
CodeCasterX marked this conversation as resolved.
Outdated
|
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
|
CodeCasterX marked this conversation as resolved.
Outdated
|
||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>modelengine.fit.jade</groupId> | ||
| <artifactId>app-builder-plugin-parent</artifactId> | ||
| <version>1.0.0-SNAPSHOT</version> | ||
| </parent> | ||
|
|
||
| <artifactId>aipp-file-extract-service</artifactId> | ||
|
|
||
| <dependencies> | ||
| <!-- FIT --> | ||
| <dependency> | ||
| <groupId>org.fitframework</groupId> | ||
| <artifactId>fit-api</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.fitframework</groupId> | ||
| <artifactId>fit-util</artifactId> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-compiler-plugin</artifactId> | ||
| <version>${maven.compiler.version}</version> | ||
| <configuration> | ||
| <source>${java.version}</source> | ||
| <target>${java.version}</target> | ||
| <encoding>${project.build.sourceEncoding}</encoding> | ||
| <compilerArgs> | ||
| <arg>-parameters</arg> | ||
| </compilerArgs> | ||
| </configuration> | ||
| </plugin> | ||
| <plugin> | ||
| <groupId>org.fitframework</groupId> | ||
| <artifactId>fit-build-maven-plugin</artifactId> | ||
| <version>${fit.version}</version> | ||
| <executions> | ||
| <execution> | ||
| <id>build-service</id> | ||
| <goals> | ||
| <goal>build-service</goal> | ||
| </goals> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
|
|
||
| </project> | ||
27 changes: 27 additions & 0 deletions
27
...t-service/src/main/java/modelengine/fit/jade/aipp/file/extract/AbstractFileExtractor.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. | ||
| * This file is a part of the ModelEngine Project. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| package modelengine.fit.jade.aipp.file.extract; | ||
|
|
||
| import modelengine.fitframework.annotation.Genericable; | ||
|
|
||
| public interface AbstractFileExtractor { | ||
|
CodeCasterX marked this conversation as resolved.
Outdated
|
||
| /** | ||
| * | ||
| * @param fileUrl 文件路径 | ||
| * @return 表示提取的文件信息的 {@link String}。 | ||
| */ | ||
| @Genericable(id = "extract-file") | ||
| String extractFile(String fileUrl); | ||
|
|
||
| /** | ||
| * | ||
| * @return 表示返回的文件枚举类型 | ||
| */ | ||
| @Genericable(id = "get-fileType") | ||
| FileTypeConstant.FileType supportedFileType(); | ||
|
|
||
| } | ||
24 changes: 24 additions & 0 deletions
24
...xtract-service/src/main/java/modelengine/fit/jade/aipp/file/extract/FileTypeConstant.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. | ||
| * This file is a part of the ModelEngine Project. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| package modelengine.fit.jade.aipp.file.extract; | ||
|
|
||
| public class FileTypeConstant { | ||
| /** | ||
| * 文件类型枚举 | ||
| */ | ||
| public enum FileType { | ||
| PDF, | ||
| WORD, | ||
| EXCEL, | ||
| IMAGE, | ||
| AUDIO, | ||
| TXT, | ||
| HTML, | ||
| MARKDOWN, | ||
| CSV | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.