feat(文档解析): 新增Word文档图片解析支持并上传至MinIO
新增对Word文档中图片的解析支持,能够提取文档中的图片并上传至MinIO存储,同时生成包含图片链接的Markdown格式文本。已支持Docx格式的Word文档处理,完善了文档解析功能。
This commit is contained in:
parent
0ff771dc19
commit
18d46ca04a
@ -11,7 +11,7 @@
|
|||||||
- 开发与生产环境隔离,构建生产镜像 <Badge type="info" text="0.4" />
|
- 开发与生产环境隔离,构建生产镜像 <Badge type="info" text="0.4" />
|
||||||
- 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
|
- 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
|
||||||
- 集成 neo4j mcp (或者自己构建工具)
|
- 集成 neo4j mcp (或者自己构建工具)
|
||||||
- 文档解析部分的 markdown 中的图片替换为内部可访问的链接
|
- 文档解析部分的 markdown 中的图片替换为内部可访问的链接(还有word等)
|
||||||
|
|
||||||
### Bugs
|
### Bugs
|
||||||
- 部分异常状态下,智能体的模型名称出现重叠[#279](https://github.com/xerrors/Yuxi-Know/issues/279)
|
- 部分异常状态下,智能体的模型名称出现重叠[#279](https://github.com/xerrors/Yuxi-Know/issues/279)
|
||||||
@ -28,6 +28,7 @@
|
|||||||
- 新增基于知识库文件生成示例问题功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425))
|
- 新增基于知识库文件生成示例问题功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425))
|
||||||
- 新增知识库支持文件夹/压缩包上传的功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425))
|
- 新增知识库支持文件夹/压缩包上传的功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425))
|
||||||
- 新增自定义模型支持、新增 dashscope rerank/embeddings 模型的支持
|
- 新增自定义模型支持、新增 dashscope rerank/embeddings 模型的支持
|
||||||
|
- 新增文档解析的图片支持,已支持 MinerU Officical、Docs、Markdown Zip格式
|
||||||
|
|
||||||
### 修复
|
### 修复
|
||||||
- 修复重排序模型实际未生效的问题
|
- 修复重排序模型实际未生效的问题
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import asyncio
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from langchain_community.document_loaders import (
|
from langchain_community.document_loaders import (
|
||||||
@ -72,6 +73,85 @@ def _extract_word_text(file_path: Path) -> str:
|
|||||||
raise ValueError(f"无法解析 Word 文档: {file_path.name}") from unstructured_error
|
raise ValueError(f"无法解析 Word 文档: {file_path.name}") from unstructured_error
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_docx_markdown_with_images(file_path: Path, params: dict | None = None) -> str:
|
||||||
|
params = params or {}
|
||||||
|
db_id = params.get("db_id") or "word-docs"
|
||||||
|
minio_client = get_minio_client()
|
||||||
|
bucket_name = "kb-images"
|
||||||
|
minio_client.ensure_bucket_exists(bucket_name)
|
||||||
|
file_id = hashstr(file_path.name, length=16)
|
||||||
|
|
||||||
|
with zipfile.ZipFile(file_path, "r") as zf:
|
||||||
|
rels_path = "word/_rels/document.xml.rels"
|
||||||
|
rid_to_target: dict[str, str] = {}
|
||||||
|
try:
|
||||||
|
rels_xml = zf.read(rels_path).decode("utf-8")
|
||||||
|
rels_root = ET.fromstring(rels_xml)
|
||||||
|
for rel in list(rels_root):
|
||||||
|
rid = rel.attrib.get("Id")
|
||||||
|
target = rel.attrib.get("Target")
|
||||||
|
rtype = rel.attrib.get("Type")
|
||||||
|
if rid and target and rtype and rtype.endswith("/image"):
|
||||||
|
rid_to_target[rid] = target
|
||||||
|
except Exception:
|
||||||
|
rid_to_target = {}
|
||||||
|
|
||||||
|
doc_xml = zf.read("word/document.xml").decode("utf-8")
|
||||||
|
ns = {
|
||||||
|
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||||
|
"a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
"r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||||
|
}
|
||||||
|
root = ET.fromstring(doc_xml)
|
||||||
|
md_lines: list[str] = []
|
||||||
|
|
||||||
|
for p in root.findall(".//w:p", ns):
|
||||||
|
texts = [t.text or "" for t in p.findall(".//w:t", ns)]
|
||||||
|
para_text = "".join(texts).strip()
|
||||||
|
image_urls: list[tuple[str, str]] = []
|
||||||
|
|
||||||
|
for blip in p.findall(".//a:blip", ns):
|
||||||
|
rid = blip.attrib.get(f"{{{ns['r']}}}embed")
|
||||||
|
if not rid:
|
||||||
|
continue
|
||||||
|
target = rid_to_target.get(rid)
|
||||||
|
if not target:
|
||||||
|
continue
|
||||||
|
media_path = target if target.startswith("word/") else f"word/{target}"
|
||||||
|
try:
|
||||||
|
data = zf.read(media_path)
|
||||||
|
object_name = f"{db_id}/{file_id}/images/{Path(target).name}"
|
||||||
|
suffix = Path(target).suffix.lower()
|
||||||
|
content_type = {
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".png": "image/png",
|
||||||
|
".gif": "image/gif",
|
||||||
|
".webp": "image/webp",
|
||||||
|
".bmp": "image/bmp",
|
||||||
|
".tif": "image/tiff",
|
||||||
|
".tiff": "image/tiff",
|
||||||
|
}.get(suffix, "image/jpeg")
|
||||||
|
result = minio_client.upload_file(
|
||||||
|
bucket_name=bucket_name,
|
||||||
|
object_name=object_name,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
)
|
||||||
|
image_urls.append((Path(target).name, result.url))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"上传图片失败 {Path(target).name}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
line = para_text
|
||||||
|
for name, url in image_urls:
|
||||||
|
line = f"{line}\n"
|
||||||
|
if line:
|
||||||
|
md_lines.append(line)
|
||||||
|
|
||||||
|
return "\n\n".join(md_lines)
|
||||||
|
|
||||||
|
|
||||||
def chunk_with_parser(file_path, params=None):
|
def chunk_with_parser(file_path, params=None):
|
||||||
"""
|
"""
|
||||||
使用文件解析器将文件切分成固定大小的块
|
使用文件解析器将文件切分成固定大小的块
|
||||||
@ -294,8 +374,11 @@ async def process_file_to_markdown(file_path: str, params: dict | None = None) -
|
|||||||
content = f.read()
|
content = f.read()
|
||||||
return f"# {file_path_obj.name}\n\n{content}"
|
return f"# {file_path_obj.name}\n\n{content}"
|
||||||
|
|
||||||
elif file_ext in [".doc", ".docx"]:
|
elif file_ext == ".docx":
|
||||||
# 处理 Word 文档
|
text = _extract_docx_markdown_with_images(file_path_obj, params=params)
|
||||||
|
return f"# {file_path_obj.name}\n\n" + text
|
||||||
|
|
||||||
|
elif file_ext == ".doc":
|
||||||
text = _extract_word_text(file_path_obj)
|
text = _extract_word_text(file_path_obj)
|
||||||
return f"# {file_path_obj.name}\n\n{text}"
|
return f"# {file_path_obj.name}\n\n{text}"
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user