11# coding=utf-8
22"""
3- @project: maxkb
4- @Author:虎
5- @file: text_split_handle.py
6- @date:2024/3/27 18:19
7- @desc:
3+ @project: maxkb
4+ @Author:虎
5+ @file: text_split_handle.py
6+ @date:2024/3/27 18:19
7+ @desc:
88"""
9+
910import io
1011import os
1112import re
1617import uuid_utils .compat as uuid
1718from charset_normalizer import detect
1819from django .utils .translation import gettext_lazy as _
20+ from knowledge .models import File
1921
2022from common .handle .base_split_handle import BaseSplitHandle
2123from common .handle .impl .text .csv_split_handle import CsvSplitHandle
2527from common .handle .impl .text .text_split_handle import TextSplitHandle
2628from common .handle .impl .text .xls_split_handle import XlsSplitHandle
2729from common .handle .impl .text .xlsx_split_handle import XlsxSplitHandle
28- from common .utils .common import parse_md_image
29- from knowledge .models import File
30+ from common .utils .common import parse_md_file_link , parse_md_image
3031
3132
3233class FileBufferHandle :
@@ -46,7 +47,7 @@ def get_buffer(self, file):
4647 XlsxSplitHandle (),
4748 XlsSplitHandle (),
4849 CsvSplitHandle (),
49- default_split_handle
50+ default_split_handle ,
5051]
5152
5253
@@ -55,7 +56,7 @@ def file_to_paragraph(file, pattern_list: List, with_filter: bool, limit: int, s
5556 for split_handle in split_handles :
5657 if split_handle .support (file , get_buffer ):
5758 return split_handle .handle (file , pattern_list , with_filter , limit , get_buffer , save_inner_image )
58- raise Exception (_ (' Unsupported file format' ))
59+ raise Exception (_ (" Unsupported file format" ))
5960
6061
6162def is_valid_uuid (uuid_str : str ):
@@ -66,102 +67,97 @@ def is_valid_uuid(uuid_str: str):
6667 return True
6768
6869
70+ def _collect_file_refs (tokens : list , base_name : str , zip_files : List [str ], content : str , update_content ):
71+ """
72+ Process a list of markdown/HTML tokens (image or file-link syntax), resolve paths against
73+ zip_files, and return (file_list, updated_content). update_content is a callable(old, new)
74+ used to patch paths in the paragraph text.
75+ """
76+ file_list = []
77+ for token in tokens :
78+ # For HTML src tags extract the src value; for markdown extract the (...) part
79+ src_match = re .search (r'\bsrc=["\']([^"\']+)["\']' , token )
80+ paren_match = re .search (r"\(([^)]*)\)" , token )
81+ if src_match :
82+ source_path = src_match .group (1 ).strip ()
83+ elif paren_match :
84+ source_path = paren_match .group (1 ).strip ().split (" " )[0 ]
85+ else :
86+ continue
87+ new_id = str (uuid .uuid7 ())
88+ file_path = urljoin (base_name , "." + source_path if source_path .startswith ("/" ) else source_path )
89+ if file_path not in zip_files :
90+ continue
91+ if file_path .startswith ("oss/file/" ) or file_path .startswith ("oss/image/" ):
92+ file_id = file_path .replace ("oss/file/" , "" ).replace ("oss/image/" , "" )
93+ if is_valid_uuid (file_id ):
94+ file_list .append ({"source_file" : file_path , "image_id" : file_id })
95+ else :
96+ file_list .append ({"source_file" : file_path , "image_id" : new_id })
97+ content = update_content (content , source_path , f"./oss/file/{ new_id } " )
98+ else :
99+ file_list .append ({"source_file" : file_path , "image_id" : new_id })
100+ content = update_content (content , source_path , f"./oss/file/{ new_id } " )
101+ return file_list , content
102+
103+
69104def get_image_list (result_list : list , zip_files : List [str ]):
70105 image_file_list = []
71106 for result in result_list :
72- for p in result .get ('content' , []):
73- content : str = p .get ('content' , '' )
74- image_list = parse_md_image (content )
75- for image in image_list :
76- search = re .search ("\(.*\)" , image )
77- if search :
78- new_image_id = str (uuid .uuid7 ())
79- source_image_path = search .group ().replace ('(' , '' ).replace (')' , '' )
80- source_image_path = source_image_path .strip ().split (" " )[0 ]
81- image_path = urljoin (result .get ('name' ), '.' + source_image_path if source_image_path .startswith (
82- '/' ) else source_image_path )
83- if not zip_files .__contains__ (image_path ):
84- continue
85- if image_path .startswith ('oss/file/' ) or image_path .startswith ('oss/image/' ):
86- image_id = image_path .replace ('oss/file/' , '' ).replace ('oss/image/' , '' )
87- if is_valid_uuid (image_id ):
88- image_file_list .append ({'source_file' : image_path ,
89- 'image_id' : image_id })
90- else :
91- image_file_list .append ({'source_file' : image_path ,
92- 'image_id' : new_image_id })
93- content = content .replace (source_image_path , f'./oss/file/{ new_image_id } ' )
94- p ['content' ] = content
95- else :
96- image_file_list .append ({'source_file' : image_path ,
97- 'image_id' : new_image_id })
98- content = content .replace (source_image_path , f'./oss/file/{ new_image_id } ' )
99- p ['content' ] = content
107+ for p in result .get ("content" , []):
108+ content : str = p .get ("content" , "" )
109+ tokens = parse_md_image (content ) + parse_md_file_link (content )
110+
111+ def _update (c , old , new ):
112+ return c .replace (old , new )
113+
114+ refs , content = _collect_file_refs (tokens , result .get ("name" ), zip_files , content , _update )
115+ image_file_list .extend (refs )
116+ p ["content" ] = content
100117
101118 return image_file_list
102119
103120
104121def get_image_list_by_content (name : str , content : str , zip_files : List [str ]):
105- image_file_list = []
106- image_list = parse_md_image (content )
107- for image in image_list :
108- search = re .search ("\(.*\)" , image )
109- if search :
110- new_image_id = str (uuid .uuid7 ())
111- source_image_path = search .group ().replace ('(' , '' ).replace (')' , '' )
112- source_image_path = source_image_path .strip ().split (" " )[0 ]
113- image_path = urljoin (name , '.' + source_image_path if source_image_path .startswith (
114- '/' ) else source_image_path )
115- if not zip_files .__contains__ (image_path ):
116- continue
117- if image_path .startswith ('oss/file/' ) or image_path .startswith ('oss/image/' ):
118- image_id = image_path .replace ('oss/file/' , '' ).replace ('oss/image/' , '' )
119- if is_valid_uuid (image_id ):
120- image_file_list .append ({'source_file' : image_path ,
121- 'image_id' : image_id })
122- else :
123- image_file_list .append ({'source_file' : image_path ,
124- 'image_id' : new_image_id })
125- content = content .replace (source_image_path , f'./oss/file/{ new_image_id } ' )
122+ tokens = parse_md_image (content ) + parse_md_file_link (content )
126123
127- else :
128- image_file_list .append ({'source_file' : image_path ,
129- 'image_id' : new_image_id })
130- content = content .replace (source_image_path , f'./oss/file/{ new_image_id } ' )
124+ def _update (c , old , new ):
125+ return c .replace (old , new )
131126
132- return image_file_list , content
127+ file_list , content = _collect_file_refs (tokens , name , zip_files , content , _update )
128+ return file_list , content
133129
134130
135131def get_file_name (file_name ):
136132 try :
137- file_name_code = file_name .encode (' cp437' )
138- charset = detect (file_name_code )[' encoding' ]
133+ file_name_code = file_name .encode (" cp437" )
134+ charset = detect (file_name_code )[" encoding" ]
139135 return file_name_code .decode (charset )
140136 except Exception as e :
141137 return file_name
142138
143139
144140def filter_image_file (result_list : list , image_list ):
145- image_source_file_list = [image .get (' source_file' ) for image in image_list ]
146- return [r for r in result_list if not image_source_file_list .__contains__ (r .get (' name' , '' ))]
141+ image_source_file_list = [image .get (" source_file" ) for image in image_list ]
142+ return [r for r in result_list if not image_source_file_list .__contains__ (r .get (" name" , "" ))]
147143
148144
149145class ZipSplitHandle (BaseSplitHandle ):
150146 def handle (self , file , pattern_list : List , with_filter : bool , limit : int , get_buffer , save_image ):
151147 if type (limit ) is str :
152148 limit = int (limit )
153149 if type (with_filter ) is str :
154- with_filter = with_filter .lower () == ' true'
150+ with_filter = with_filter .lower () == " true"
155151 buffer = get_buffer (file )
156152 bytes_io = io .BytesIO (buffer )
157153 result = []
158154 # 打开zip文件
159- with zipfile .ZipFile (bytes_io , 'r' ) as zip_ref :
155+ with zipfile .ZipFile (bytes_io , "r" ) as zip_ref :
160156 # 获取压缩包中的文件名列表
161157 files = zip_ref .namelist ()
162158 # 读取压缩包中的文件内容
163159 for file in files :
164- if file .endswith ('/' ) or file .startswith (' __MACOSX' ):
160+ if file .endswith ("/" ) or file .startswith (" __MACOSX" ):
165161 continue
166162 with zip_ref .open (file ) as f :
167163 # 对文件内容进行处理
@@ -179,11 +175,11 @@ def handle(self, file, pattern_list: List, with_filter: bool, limit: int, get_bu
179175 result = filter_image_file (result , image_list )
180176 image_mode_list = []
181177 for image in image_list :
182- with zip_ref .open (image .get (' source_file' )) as f :
178+ with zip_ref .open (image .get (" source_file" )) as f :
183179 i = File (
184- id = image .get (' image_id' ),
185- file_name = os .path .basename (image .get (' source_file' )),
186- meta = {' debug' : False , ' content' : f .read ()} # 这里的content是二进制数据
180+ id = image .get (" image_id" ),
181+ file_name = os .path .basename (image .get (" source_file" )),
182+ meta = {" debug" : False , " content" : f .read ()}, # 这里的content是二进制数据
187183 )
188184 image_mode_list .append (i )
189185 save_image (image_mode_list )
@@ -200,16 +196,16 @@ def get_content(self, file, save_image):
200196 从 zip 中提取并返回拼接的 md 文本,同时收集并保存内嵌图片(通过 save_image 回调)。
201197 使用 posixpath 来正确处理 zip 内部的路径拼接与规范化。
202198 """
203- buffer = file .read () if hasattr (file , ' read' ) else None
199+ buffer = file .read () if hasattr (file , " read" ) else None
204200 bytes_io = io .BytesIO (buffer ) if buffer is not None else io .BytesIO (file )
205201 image_list = []
206202 content_parts = []
207203
208- with zipfile .ZipFile (bytes_io , 'r' ) as zip_ref :
204+ with zipfile .ZipFile (bytes_io , "r" ) as zip_ref :
209205 files = zip_ref .namelist ()
210206 file_content_list = []
211207 for inner_name in files :
212- if inner_name .endswith ('/' ) or inner_name .startswith (' __MACOSX' ):
208+ if inner_name .endswith ("/" ) or inner_name .startswith (" __MACOSX" ):
213209 continue
214210 with zip_ref .open (inner_name ) as zf :
215211 try :
@@ -223,11 +219,12 @@ def get_content(self, file, save_image):
223219 if split_handle .support (zf , get_buffer ):
224220 row = get_buffer (zf )
225221 md_text = split_handle .get_content (io .BytesIO (row ), save_image )
226- file_content_list .append ({' content' : md_text , ' name' : real_name })
222+ file_content_list .append ({" content" : md_text , " name" : real_name })
227223 break
228224 for file_content in file_content_list :
229- _image_list , content = get_image_list_by_content (file_content .get ('name' ), file_content .get ("content" ),
230- files )
225+ _image_list , content = get_image_list_by_content (
226+ file_content .get ("name" ), file_content .get ("content" ), files
227+ )
231228 content_parts .append (content )
232229 for image in _image_list :
233230 image_list .append (image )
@@ -236,13 +233,13 @@ def get_content(self, file, save_image):
236233 if image_list :
237234 image_mode_list = []
238235 for image in image_list :
239- with zip_ref .open (image .get (' source_file' )) as f :
236+ with zip_ref .open (image .get (" source_file" )) as f :
240237 i = File (
241- id = image .get (' image_id' ),
242- file_name = os .path .basename (image .get (' source_file' )),
243- meta = {' debug' : False , ' content' : f .read ()} # 这里的content是二进制数据
238+ id = image .get (" image_id" ),
239+ file_name = os .path .basename (image .get (" source_file" )),
240+ meta = {" debug" : False , " content" : f .read ()}, # 这里的content是二进制数据
244241 )
245242 image_mode_list .append (i )
246243 save_image (image_mode_list )
247244
248- return ' \n \n ' .join (content_parts )
245+ return " \n \n " .join (content_parts )
0 commit comments