kgrag/app_pkg/markdown_process/markdown_preprocess.py
2026-06-30 13:35:52 +08:00

298 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import UploadFile
import httpx
import os
from typing import Optional
import tempfile
import requests
import time
from tqdm import tqdm
from pathlib import Path
import json
from io import BytesIO
from urllib.parse import urljoin
async def process_pdf_logic(pdf_file):
"""
接收 PDF 文件并转发到解析服务
"""
# 验证文件类型
if not pdf_file.content_type.startswith("application/pdf"):
raise Exception(400, "仅支持 PDF 文件")
try:
# 读取文件内容
# file_content = await pdf_file.read()
# 配置目标接口参数
target_url = os.getenv("Mineru_URL", "http://172.18.30.122:9988")
target_url = target_url + "/analyze-pdf"
# target_url = os.getenv("Mineru_URL","http://172.18.127.124:9088/pdf_parse")
# target_url = "http://172.18.30.122:8998/pdf_parse"
# params = {
# "parse_method": "auto",
# "is_json_md_dump": "true",
# "output_dir": "output"
# }
# 构建文件上传格式
files = {
"file": (pdf_file.filename, pdf_file.file, "application/pdf")
# "pdf_file": (pdf_file.filename, pdf_file.file, "application/pdf")
}
# 发送请求到解析服务
async with httpx.AsyncClient(timeout=1200.0) as client:
response = await client.post(
target_url,
# params=params,
files=files,
headers={"Accept": "application/json"}
)
response.raise_for_status()
result = response.json()
content_chunk_list = []
full_content = result.get("data", {}).get("full_content", "")
content_list = result.get("data", {}).get("content_list", [])
content_chunk_list = result.get("data", {}).get("content_middle", [])
print(content_chunk_list)
# combined_json = ''.join(content_list)
# content_data = json.loads(combined_json)
# for idx, item in enumerate(content_data):
# if isinstance(item, dict): # 确保是字典类型
# text_type = item.get("type", "")
# text_level = item.get("text_level", "")
# text = item.get("text", "")
# page_idx = item.get("page_idx", -1) # 默认值 -1 表示缺失
# table_caption = item.get("table_caption", "")
# table_body = item.get("table_body", "")
# image_path = item.get("img_path", "")
# full_url = ""
# if image_path:
# base_url = os.getenv("HOST_IP", "http://172.18.30.122:9988") # 基础URL不带/outputs
# outputs_url = urljoin(base_url + "/", "output/")
# full_url = urljoin(outputs_url, image_path) # 最终拼接image_path
# if not text and not table_body and not image_path:
# continue
# image_caption = item.get("img_caption", "")
# content_chunk_list.append(
# {"id": idx, "type": text_type, "text_level": text_level, "text": text, "page_idx": page_idx,
# "table_caption": table_caption, "table_body": table_body, "image_path": full_url,
# "image_caption": image_caption})
# markdown_content =""
# for content in content_list:
# text = content["text"]
return full_content, content_chunk_list
except httpx.RequestError as e:
raise Exception(503, f"无法连接到解析服务: {str(e)}")
except Exception as e:
raise Exception(500, f"服务器内部错误: {str(e)}")
async def process_doc_logic(doc_file):
"""
接收Word文档(doc/docx)并转发到解析服务
"""
# 验证文件类型
allowed_extensions = {'.doc', '.docx'}
file_ext = os.path.splitext(doc_file.filename)[1].lower()
if file_ext not in allowed_extensions:
raise Exception(400, "仅支持doc或docx格式文件")
try:
# 配置目标接口参数
target_url = os.getenv("MINERU_URL", "http://172.18.30.122:9988")
target_url = target_url + "/analyze-doc"
# 根据文件类型设置正确的MIME类型
mime_type = "application/msword" if file_ext == '.doc' else "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
# 构建文件上传格式
files = {
"file": (doc_file.filename, doc_file.file, mime_type)
}
# 发送请求到解析服务
async with httpx.AsyncClient(timeout=120.0) as client:
response = await client.post(
target_url,
files=files,
headers={"Accept": "application/json"}
)
response.raise_for_status()
result = response.json()
# 假设返回数据结构与PDF不同需要相应调整
# 这里根据实际API返回结构调整
return result["data"]["full_content"]
except httpx.HTTPStatusError as e:
raise Exception(e.response.status_code, f"文档解析服务返回错误: {str(e)}")
except httpx.RequestError as e:
raise Exception(503, f"无法连接到解析服务: {str(e)}")
except Exception as e:
raise Exception(500, f"服务器内部错误: {str(e)}")
class ProgressFileReader:
"""带有进度条的文件读取器"""
def __init__(self, file_path, progress_bar):
self.file = open(file_path, 'rb')
self.progress_bar = progress_bar
def read(self, size=-1):
data = self.file.read(size)
if data:
self.progress_bar.update(len(data))
return data
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
async def convert_doc_to_pdf(api_url: str, file: UploadFile, timeout: int = 300) -> Optional[str]:
"""
将DOC文件转换为pdf
参数:
api_url: 转换服务的URL (如 "http://172.18.127.124:8000")
file: 要转换的UploadFile对象
timeout: 请求超时时间(秒)
"""
try:
# 创建临时文件保存上传的DOC
with tempfile.NamedTemporaryFile(delete=False, suffix=".doc") as tmp_doc:
content = await file.read()
tmp_doc.write(content)
tmp_path = tmp_doc.name
# 获取文件信息
file_name = file.filename
file_size = os.path.getsize(tmp_path)
output_name = Path(file_name).stem + ".pdf"
# 使用带进度条的文件读取器
with tqdm(total=file_size, unit='B', unit_scale=True, desc="上传进度") as pbar:
with ProgressFileReader(tmp_path, pbar) as file_reader:
files = {'file': (file_name, file_reader)}
params = {'src': 'doc', 'to': 'pdf'}
response = requests.post(
f"{api_url}/transfer",
files=files,
params=params,
timeout=timeout
)
# 处理响应
if response.status_code == 200:
# 创建临时文件保存转换后的DOCX
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_docx:
tmp_docx.write(response.content)
output_path = tmp_docx.name
# 清理临时文件
os.unlink(tmp_path)
return output_path
else:
raise Exception(f"转换失败: {response.text}")
except requests.exceptions.Timeout:
raise Exception(f"请求超时(超过{timeout}秒)")
except requests.exceptions.RequestException as e:
raise Exception(f"请求错误: {str(e)}")
except Exception as e:
# 确保临时文件被清理
if 'tmp_path' in locals() and os.path.exists(tmp_path):
os.unlink(tmp_path)
if 'output_path' in locals() and os.path.exists(output_path):
os.unlink(output_path)
raise Exception(f"转换过程中出错: {str(e)}")
async def convert_docx_to_pdf(api_url: str, file: UploadFile, timeout: int = 300) -> Optional[str]:
"""
将DOCX文件转换为PDF解决中文缺失问题
参数:
api_url: 转换服务的URL (如 "http://172.18.127.124:8000")
file: 要转换的UploadFile对象
timeout: 请求超时时间(秒)
"""
try:
# 创建临时文件保存上传的DOCX
with tempfile.NamedTemporaryFile(delete=False, suffix=".docx") as tmp_docx:
content = await file.read()
tmp_docx.write(content)
tmp_path = tmp_docx.name
# 获取文件信息
file_name = file.filename
file_size = os.path.getsize(tmp_path)
output_name = Path(file_name).stem + ".pdf"
print(f"正在转换: {file_name} ({file_size / 1024:.1f} KB) -> PDF")
start_time = time.time()
# 使用带进度条的文件读取器
with tqdm(total=file_size, unit='B', unit_scale=True, desc="上传进度") as pbar:
with ProgressFileReader(tmp_path, pbar) as file_reader:
files = {'file': (file_name, file_reader)}
params = {
'src': 'docx',
'to': 'pdf',
'font': 'SimSun', # 指定中文字体
'encoding': 'utf-8' # 明确编码
}
response = requests.post(
f"{api_url}/transfer",
files=files,
params=params,
timeout=timeout
)
# 处理响应
if response.status_code == 200:
# 创建临时文件保存转换后的PDF
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_pdf:
tmp_pdf.write(response.content)
output_path = tmp_pdf.name
# 验证PDF是否包含文字
if os.path.getsize(output_path) < 1024: # 如果PDF文件过小
raise Exception("生成的PDF可能不包含有效内容")
elapsed = time.time() - start_time
print(f"\n转换成功! 保存为: {output_path}")
print(f"总耗时: {elapsed:.2f}")
# 清理临时文件
os.unlink(tmp_path)
return output_path
else:
raise Exception(f"转换失败: {response.text}")
except requests.exceptions.Timeout:
raise Exception(f"请求超时(超过{timeout}秒)")
except requests.exceptions.RequestException as e:
raise Exception(f"请求错误: {str(e)}")
except Exception as e:
# 确保临时文件被清理
if 'tmp_path' in locals() and os.path.exists(tmp_path):
os.unlink(tmp_path)
if 'output_path' in locals() and os.path.exists(output_path):
os.unlink(output_path)
raise Exception(f"转换过程中出错: {str(e)}")