Spring AI:提取 txt、Json、Markdown、Html、Pdf 文件数据,转换为 Document 文档

历史文章

Spring AI:对接DeepSeek实战
Spring AI:对接官方 DeepSeek-R1 模型 ------ 实现推理效果
Spring AI:ChatClient实现对话效果
Spring AI:使用 Advisor 组件 - 打印请求大模型出入参日志
Spring AI:ChatMemory 实现聊天记忆功能
Spring AI:本地安装 Ollama 并运行 Qwen3 模型
Spring AI:提示词工程
Spring AI:提示词工程 - Prompt 角色分类(系统角色与用户角色)
Spring AI:基于 "助手角色" 消息实现聊天记忆功能
Spring AI:结构化输出 - 大模型响应内容
Spring AI:Docker 安装 Cassandra 5.x(限制内存占用)&& CQL
Spring AI:整合 Cassandra - 实现聊天消息持久化
Spring AI:多模态 AI 大模型
Spring AI:文生图:调用通义万相 AI 大模型
Spring AI:文生音频 - cosyvoice-V2
Spring AI:文生视频 - wanx2.1-i2v-plus
Spring AI:上手体验工具调用(Tool Calling)
Spring AI:整合 MCP Client - 调用高德地图 MCP 服务
Spring AI:搭建自定义 MCP Server:获取 QQ 信息
Spring AI:对接自定义 MCP Server
Spring AI:RAG 增强检索介绍
Spring AI:Docker 安装向量数据库 - Redis Stack
Spring AI:文档向量化存储与检索

上文中,我们在项目初始化时,手动创建了 3 条 Document 文档,然后存储到了 Redis 向量数据库中。

但是实际应用场景中,可能需要读取不同格式的文档,如 txt 、Json 、Markdown 、Html 、Pdf 等等。本文中,我们就来尝试从不同格式文件读取数据,并转换为 Document 文档。

提取 txt 文件

在 /resources 资源目录下,创建一个 /document 文件夹,用于放置本不同格式的数据源文件。

先在里面创建一个名为 manual.txt 的冰箱说明书文件,内容如下:

海尔BCD-520WDPD对开门冰箱使用说明书

java 复制代码
1. 安全注意事项
- 请将冰箱放置在通风良好、远离热源的位置
- 确保电源电压与冰箱额定电压(220V~50Hz)相符
- 新冰箱首次使用前应静置2小时以上再通电

2. 部件说明
① 冷藏室(4-8℃)
② 变温室(-3~4℃可调)
③ 冷冻室(-18℃以下)
④ 智能控制面板
⑤ 可调节搁架

3. 基本操作
- 温度设置:
  短按"温区选择"键切换温区 → 按"温度调节"键设置(冷藏:2-8℃,冷冻:-16~-24℃)
- 速冷功能:冷藏室温度快速降至2℃(持续6小时后自动退出)
- 速冻功能:冷冻室快速制冷(持续24小时后自动退出)

4. 日常使用建议
✓ 热食应冷却至室温后再放入
✓ 食物存放请保留1cm以上间隙保证冷气循环
✓ 每月清洁一次密封条(用中性清洁剂+软布)
✓ 长期不用时应断电、除霜、清洁并保持门体微开

5. 故障处理
◆ 异常报警音:
  - 连续3声:门未关严(检查门封)
  - 连续6声:传感器故障(联系售后)
◆ 冷藏室结霜:检查门封密封性
◆ 噪音过大:确认冰箱是否放置平稳

6. 技术参数
额定功率:120W
总容积:520L(冷藏室326L+冷冻室194L)
噪音值:38dB
能效等级:1级
净重:98kg

(本产品符合GB 4706.1和GB 4706.13国家标准)

接着,如下图所示,新建一个 /reader 包,用于放置读取不同格式文件的阅读器类。先在包内创建一个 MyTextReader 阅读器,用于读取 txt 文件:

java 复制代码
@Component
public class MyTextReader {

    @Value("classpath:/document/manual.txt")
    private Resource resource;

    /**
     * 读取 Txt 文档
     * @return
     */
    public List<Document> loadText() {
        // 创建 TextReader 对象,用于读取指定资源 (resource) 的文本内容
        TextReader textReader = new TextReader(resource);
        // 添加自定义元数据,如文件名称
        textReader.getCustomMetadata()
                .put("filename", "manual.txt");
        // 读取并转换为 Document 文档集合
        return textReader.read();
    }

    /**
     * 读取 Txt 文档并分块拆分
     * @return
     */
    public List<Document> loadTextAndSplit() {
        // 创建 TextReader 对象,用于读取指定资源 (resource) 的文本内容
        TextReader textReader = new TextReader(resource);

        // 将资源内容解析为 Document 对象集合
        List<Document> documents = textReader.get();

        // 使用 TokenTextSplitter 对文档列表进行分块处理
        List<Document> splitDocuments = new TokenTextSplitter().apply(documents);

        // 返回拆分后的文档分块集合
        return splitDocuments;
    }
}

解释一下上述代码:

  • 通过 @Value("classpath:/document/manual.txt") 注解,读取 /resources 目录下的 manual.txt , 并注入到 Resource 资源类中;

  • 定义 loadText() 方法 : 读取 txt 文件,转换为 Document 文档集合,在不做任何中间处理的情况下,集合大小为 1, 是将整个文本作为一条文档返回;

  • 定义 loadTextAndSplit() : 对于体积非常大的 txt 文件,你可能需要将文本拆分为更小的块,例如使用像 TokenTextSplitter 这样的文本分割器;

为了测试一下效果,我们在 /controller 包下,新建一个 ReaderController 控制器,并声明 /read/txt 和 /read/txt2 两个接口,分别测试一下上述阅读器中定义的两个方法:

java 复制代码
@RestController
@RequestMapping("/read")
public class ReaderController {

    @Resource
    private MyTextReader textReader;

    @GetMapping(value = "/txt")
    public List<Document> readText() {
        return textReader.loadText();
    }

    @GetMapping(value = "/txt2")
    public List<Document> readText2() {
        return textReader.loadTextAndSplit();
    }

}

重启后端项目,先请求 /read/txt 接口,观察返回的 Document 文档数据,效果如下:

然后是请求 /read/txt2 接口,测试一下文档分块效果,如下:

读取 Json 文件

在 /document 文件下,再新建一个 tv.json 文件:

java 复制代码
[
  {
    "id": 1,
    "title": "狂飙",
    "description": "2023年现象级扫黑刑侦剧,讲述刑警与黑恶势力长达二十年的生死较量,张译张颂文双雄对决引爆全网。"
  },
  {
    "id": 2,
    "title": "繁花",
    "description": "王家卫执导的上海商战年代剧,胡歌演绎90年代股市浮沉,沪语对白与胶片质感掀起怀旧热潮。"
  },
  {
    "id": 3,
    "title": "长相思",
    "description": "古装神话爆款,杨紫张晚意演绎上古爱恨纠葛,唯美虐恋单日播放量破亿,登顶年度仙侠TOP。"
  },
  {
    "id": 4,
    "title": "庆余年第二季",
    "description": "权谋神剧续作,张若昀陈道明朝堂博弈,开播即打破平台热度纪录,反转剧情引爆社交媒体。"
  }
]

然后,在 /reader 包下,新建一个 MyJsonReader 阅读器,代码如下:

java 复制代码
@Component
public class MyJsonReader {

    @Value("classpath:/document/tv.json")
    private Resource resource;

    /**
     * 读取 Json 文件
     * @return
     */
    public List<Document> loadJson() {
        // 创建 JsonReader 阅读器实例,配置需要读取的字段
        JsonReader jsonReader = new JsonReader(resource, "description", "content", "title");
        // 执行读取操作,并转换为 Document 对象集合
        return jsonReader.get();
    }

}

接着,编辑 ReaderController 控制器,声明一个 /read/json 接口,新增代码如下:

java 复制代码
@RestController
@RequestMapping("/read")
public class ReaderController {

	// 省略...
	
    @Resource
    private MyJsonReader jsonReader;

	// 省略...

    @GetMapping(value = "/json")
    public List<Document> readJson() {
        return jsonReader.loadJson();
    }

}

重启后端项目,浏览器请求上述接口,效果如下:

读取 Markdown

再来试试读取 Markdown 格式的文档。编辑 pom.xml , 添加如下依赖:

java 复制代码
<!-- 读取 Markdown -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-markdown-document-reader</artifactId>
</dependency>

添加完成后,记得刷新一下 Maven 依赖,将包下载到本地仓库中。

接着,在 /document 文件夹下,新建一个 code.md 文件,文档内容如下:

以下是一个自定义安全配置类的 Java 实现:

java 复制代码
package com.example.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            );
        
        return http.build();
    }
}


在安全配置中,`HttpSecurity` 对象允许我们定义细粒度的访问控制规则,这种声明式配置方式比传统的 XML 配置更简洁。

要测试安全配置,可使用以下 curl 命令模拟登录请求:


curl -X POST http://localhost:8080/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=admin&password=secret"


> **注意**:实际部署时应使用加密密码存储,示例中为简化展示使用明文

继续在 /reader 包下,新建一个 MyMarkdownReader 阅读器,代码如下:

java 复制代码
@Component
public class MyMarkdownReader {

    @Value("classpath:/document/code.md")
    private Resource resource;

    public List<Document> loadMarkdown() {
        // MarkdownDocumentReader 阅读器配置类
        MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
                .withHorizontalRuleCreateDocument(true) // 遇到水平线 ---,则创建新文档
                .withIncludeCodeBlock(false) // 排除代码块(代码块生成单独文档)
                .withIncludeBlockquote(false) // 排除块引用(块引用生成单独文档)
                .withAdditionalMetadata("filename", "code.md") // 添加自定义元数据,如文件名称
                .build();

        // 新建 MarkdownDocumentReader 阅读器
        MarkdownDocumentReader reader = new MarkdownDocumentReader(resource, config);
        
        // 读取并转换为 Document 文档集合
        return reader.get();
    }
}

编辑 ReaderController 控制器,声明一个 /read/md 接口,新增代码如下:

java 复制代码
@RestController
@RequestMapping("/read")
public class ReaderController {

    // 省略...
    
    @Resource
    private MyMarkdownReader markdownReader;

    // 省略...

    @GetMapping(value = "/md")
    public List<Document> readMarkdown() {
        return markdownReader.loadMarkdown();
    }

}

重启后端项目,浏览器请求上面的接口。

读取 Html

再来试试读取 HTML 格式文档。编辑 pom.xml, 添加如下依赖:

java 复制代码
<!-- 读取 HTML -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-jsoup-document-reader</artifactId>
</dependency>

添加完成后,记得刷新一下 Maven 依赖。

紧接着,在 /document 文件夹中,新增一个名为 my-page.html 的文件,内容如下:

java 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>我的网页</title>
    <meta name="description" content="Spring AI示例网页">
    <meta name="keywords" content="spring, ai, html, 示例">
    <meta name="author" content="犬小哈">
    <meta name="date" content="2025-01-15">
    <link rel="stylesheet" href="style.css">
</head>
<body>
<header>
    <h1>欢迎来到我的网页</h1>
</header>
<nav>
    <ul>
        <li><a href="/">首页</a></li>
        <li><a href="/about">关于</a></li>
    </ul>
</nav>
<article>
    <h2>主要内容</h2>
    <p>这是我的网页的主要内容。</p>
    <p>它包含多个段落。</p>
    <a href="https://www.example.com">外部链接</a>
</article>
<footer>
    <p>© 2025 小马</p>
</footer>
</body>
</html>

在 /reader 包下,新建一个 MyHtmlReader 阅读器,代码如下:

java 复制代码
@Component
public class MyHtmlReader {

    @Value("classpath:/document/my-page.html")
    private Resource resource;

    public List<Document> loadHtml() {
        // JsoupDocumentReader 阅读器配置类
        JsoupDocumentReaderConfig config = JsoupDocumentReaderConfig.builder()
                .selector("article p") // 提取 <article> 标签内的 p 段落
                .charset("UTF-8") // 使用 UTF-8 编码
                .includeLinkUrls(true) // 在元数据中包含链接 URL(绝对链接)
                .metadataTags(List.of("author", "date")) // 提取 author 和 date 元标签
                .additionalMetadata("source", "my-page.html") // 添加自定义元数据
                .build();

        // 新建 JsoupDocumentReader 阅读器
        JsoupDocumentReader reader = new JsoupDocumentReader(resource, config);

        // 读取并转换为 Document 文档集合
        return reader.get();
    }
}

编辑 ReaderController 控制器,声明 /read/html 接口,新增代码如下:

java 复制代码
@RestController
@RequestMapping("/read")
public class ReaderController {

    // 省略...
    
    @Resource
    private MyHtmlReader htmlReader;

    // 省略...

    @GetMapping(value = "/html")
    public List<Document> readHtml() {
        return htmlReader.loadHtml();
    }

}

重启后端项目,浏览器请求接口。

读取 Pdf

最后再来试试读取 Pdf 格式文件。编辑 pom.xml , 添加如下依赖:

java 复制代码
<!-- 读取 PDF -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>

准备一个pdf文件

下载完成后,将其复制到 /document 文件夹下,并重命名为 profile.pdf 。

在 /reader 包下,新建一个 MyPdfReader 阅读器,代码如下:

java 复制代码
@Component
public class MyPdfReader {

    public List<Document> getDocsFromPdf() {
        // 新建 PagePdfDocumentReader 阅读器
        PagePdfDocumentReader pdfReader = new PagePdfDocumentReader("classpath:/document/profile.pdf", // PDF 文件路径
                PdfDocumentReaderConfig.builder()
                        .withPageTopMargin(0) // 设置页面顶边距为0
                        .withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
                                .withNumberOfTopTextLinesToDelete(0) // 设置删除顶部文本行数为0
                                .build())
                        .withPagesPerDocument(1) // 设置每个文档包含1页
                        .build());

        // 读取并转换为 Document 文档集合
        return pdfReader.read();
    }
}

编辑 ReaderController 控制器,声明一个 /read/pdf 接口,新增代码如下:

java 复制代码
@RestController
@RequestMapping("/read")
public class ReaderController {

    // 省略...
    
    @Resource
    private MyPdfReader pdfReader;

    // 省略...

    @GetMapping(value = "/pdf")
    public List<Document> readPdf() {
        return pdfReader.getDocsFromPdf();
    }

}

重启后端项目,浏览器请求上述接口,观察返回的文档集合。

相关推荐
九狼14 分钟前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS22 分钟前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区2 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈2 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang2 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx
shengjk13 小时前
NanoClaw 深度剖析:一个"AI 原生"架构的个人助手是如何运转的?
人工智能
西门老铁5 小时前
🦞OpenClaw 让 MacMini 脱销了,而我拿出了6年陈的安卓机
人工智能
NE_STOP6 小时前
springMVC-HTTP消息转换器与文件上传、下载、异常处理
spring
恋猫de小郭6 小时前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程