b_knowledgenote.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. from flask import flash, render_template, session, request, redirect, url_for
  2. from admin.knowledgetopic import *
  3. from admin.knowledgenote import *
  4. from flask import current_app as app
  5. import os
  6. import zipfile
  7. import shutil
  8. import re
  9. from hashlib import md5
  10. from pathlib import Path
  11. from datetime import datetime
  12. from werkzeug.utils import secure_filename
  13. from urllib.parse import urlparse
  14. ALLOWED_EXTENSIONS_IMG = set(['png', 'jpg', 'JPG', 'PNG', 'bmp'])
  15. ALLOWED_EXTENSIONS_VIDEO = set(['mp4', 'avi', 'flv', 'wmv', 'wov', 'mkv'])
  16. ALLOWED_EXTENSIONS_ZIP = set(['zip', 'ZIP']) # 新增ZIP文件格式支持
  17. # 判断文件格式(图像)
  18. def allowed_file_img(filename):
  19. return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS_IMG
  20. # 判断文件格式(视频)
  21. def allowed_file_video(filename):
  22. return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS_VIDEO
  23. # 判断文件格式(ZIP)
  24. def allowed_file_zip(filename):
  25. return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS_ZIP
  26. # ------------- 笔记信息管理 -------------------
  27. @app.route('/knowledgenotemanage')
  28. def knowledgenotemanage():
  29. if 'username' in session:
  30. results = loadAllKnowledgeNote()
  31. return render_template('admin/a_knowledgenote_manage.html', results=results)
  32. else:
  33. flash('登录失败, 请重新登录')
  34. return redirect(url_for('adminlogin'))
  35. @app.route('/newknowledgenote', methods=['POST', 'GET'])
  36. def newknowledgenote():
  37. if 'username' in session:
  38. knowledgetopic = loadAllKnowledgeTopic()
  39. return render_template('admin/a_knowledgenote_edit.html', knowledgetopic=knowledgetopic, knowledgenote=None,
  40. status='add')
  41. else:
  42. flash('登录失败, 请重新登录')
  43. return redirect(url_for('adminlogin'))
  44. @app.route('/editknowledgenote/<knid>', methods=['POST', 'GET'])
  45. def editnoteinfo(knid):
  46. if 'username' in session:
  47. knowledgetopic = loadAllKnowledgeTopic()
  48. knowledgenote = loadKnowledgeNoteByKNID(knid)
  49. return render_template('admin/a_knowledgenote_edit.html', knowledgetopic=knowledgetopic,
  50. knowledgenote=knowledgenote, status='edit')
  51. else:
  52. flash('登录失败, 请重新登录')
  53. return redirect(url_for('adminlogin'))
  54. @app.route('/editknowledgenote.do', methods=['POST'])
  55. def addnoteinfo():
  56. if 'username' in session:
  57. if request.method == 'POST':
  58. status = request.form['lbl_status']
  59. ktid = request.form.get('txt_KTID')
  60. kntitle = request.form['txt_KNTitle']
  61. isoriginal = request.form['txt_IsOriginal']
  62. source = request.form['txt_Source']
  63. origlink = request.form['txt_OrigLink']
  64. knimage = request.form['txt_KNImage']
  65. knabstract = request.form['txt_KNAbstract']
  66. kncontent = eval(request.form['txt_KNcontent'])
  67. kncontent = kncontent.replace('''"''', r'''\"''').replace("'", r"\'")
  68. filelink = request.form['txt_FileLink']
  69. pptlink = request.form['txt_PPTLink']
  70. videolink = request.form['txt_VideoLink']
  71. codelink = request.form['txt_CodeLink']
  72. datalink = request.form['txt_DataLink']
  73. author = request.form['txt_CoAuthors']
  74. knisshow = request.form.get('txt_KNIsShow')
  75. # 封面图片处理
  76. f = request.files['file_proimg']
  77. proimg = ""
  78. if f and f.filename != "":
  79. proimg = uploadimage(f)
  80. if proimg == "":
  81. proimg = knimage
  82. # 视频上传处理
  83. fvideo = request.files['file_video']
  84. video = ""
  85. if fvideo and fvideo.filename != "":
  86. video = uploadvideo(fvideo)
  87. if video == "":
  88. video = videolink
  89. if status == 'add':
  90. data = [ktid, kntitle, isoriginal, source, origlink, proimg, knabstract, kncontent, filelink, pptlink,
  91. video, codelink, datalink, author, knisshow]
  92. i, msg = add_KnowledgeNote(data)
  93. if i > 0:
  94. flash('笔记信息添加成功!')
  95. return redirect(url_for('knowledgenotemanage'))
  96. else:
  97. flash('笔记信息添加失败!%s' % str(msg))
  98. return redirect(url_for('knowledgenotemanage'))
  99. if status == 'edit':
  100. knid = request.form['lbl_KNID']
  101. data = [ktid, kntitle, isoriginal, source, origlink, proimg, knabstract, kncontent, filelink, pptlink,
  102. video, codelink, datalink, author, knisshow, knid]
  103. i, msg = edit_KnowledgeNote(data)
  104. if i > 0:
  105. flash('笔记信息修改成功!')
  106. return redirect(url_for('knowledgenotemanage'))
  107. else:
  108. flash('笔记信息修改失败!%s' % str(msg))
  109. return redirect(url_for('knowledgenotemanage'))
  110. else:
  111. flash('登录失败, 请重新登录')
  112. return redirect(url_for('adminlogin'))
  113. # ============ ZIP文件上传和解压功能 ============
  114. def upload_and_process_md_zip():
  115. """
  116. 简化的MD ZIP文件上传处理函数,整合了所有功能
  117. """
  118. try:
  119. if 'file' not in request.files: # 1. 检查文件上传
  120. return {"success": False, "message": "没有上传zip文件"}
  121. if 'folderName' not in request.form:
  122. return {"success": False, "message": "没有上传文件夹名"}
  123. file = request.files['file']
  124. folderName = request.form.get('folderName')
  125. if file.filename == '':
  126. return {"success": False, "message": "请选择要上传的ZIP文件"}
  127. if not ('.' in file.filename and file.filename.rsplit('.', 1)[1].lower() == 'zip'): #验证是否zip
  128. return {"success": False, "message": "只支持ZIP格式的压缩包"}
  129. original_filename = secure_filename(file.filename)#安全保存文件
  130. file_digest = md5(original_filename.encode("utf-8")).hexdigest()
  131. zip_filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{file_digest}.zip"
  132. zip_path = os.path.join('temp_uploads', zip_filename)
  133. os.makedirs(os.path.dirname(zip_path), exist_ok=True)
  134. file.save(zip_path)
  135. # 4. 安全解压ZIP文件
  136. extract_dir_name = Path(original_filename).stem + datetime.now().strftime('%Y%m%d%H%M%S')
  137. print(extract_dir_name)
  138. extract_path = os.path.join('static/uploads/md', extract_dir_name)
  139. os.makedirs(extract_path, exist_ok=True)
  140. if not zipfile.is_zipfile(zip_path):
  141. os.remove(zip_path)
  142. return {"success": False, "message": "无效的ZIP文件"}
  143. extracted_files = []
  144. with zipfile.ZipFile(zip_path, 'r') as zip_ref:
  145. for file_info in zip_ref.infolist():
  146. if file_info.filename.endswith('/'):
  147. continue
  148. # 安全路径检查
  149. target_path = os.path.join(extract_path, file_info.filename)
  150. if not os.path.realpath(target_path).startswith(os.path.realpath(extract_path)):
  151. os.remove(zip_path)
  152. return {"success": False, "message": f"检测到不安全路径: {file_info.filename}"}
  153. # 解压文件
  154. zip_ref.extract(file_info, extract_path)
  155. extracted_files.append(target_path)
  156. # 5. 查找并处理MD文件
  157. md_files = []
  158. for root, dirs, files in os.walk(extract_path):
  159. for filename in files:
  160. if filename.lower().endswith('.md'):
  161. full_path = os.path.join(root, filename)
  162. relative_path = os.path.relpath(full_path, extract_path)
  163. md_files.append({'full_path': full_path, 'relative_path': relative_path})
  164. # 6. 检查MD文件数量
  165. if len(md_files) == 0:
  166. return {
  167. "success": True,
  168. "message": "ZIP文件解压成功,但未找到MD文件",
  169. "file_count": 0
  170. }
  171. # 7. 处理MD文件内容
  172. for md_file in md_files:
  173. replace_image_paths( md_file['full_path'],folderName,extract_dir_name) #传入处理md的函数
  174. # 8. 清理临时文件并返回结果
  175. os.remove(zip_path)
  176. return {
  177. 'success': True,
  178. "md_file":md_file,
  179. 'extract_url': f"/static/uploads/md/{extract_dir_name}"
  180. }
  181. except Exception as e:
  182. return {"success": False, "message": f"处理过程中出错: {str(e)}"}
  183. def replace_image_paths(file_path, folder_name,extract_dir_name, new_base_path="/static/uploads/md/"):
  184. """
  185. 原先设定正则提取url,文件夹名不变,提取文件夹名前面和文件夹名后面的字符串,发现有问题,重新进行修改
  186. """
  187. try:
  188. # 读取Markdown文件内容
  189. with open(file_path, 'r', encoding='utf-8') as f:
  190. content = f.read()
  191. # 正则表达式匹配Markdown图片语法和HTML img标签
  192. patterns = [
  193. r'!\[.*?\]\((.*?)\)', # Markdown图片
  194. r'<img[^>]+src="([^">]+)"' # HTML img标签
  195. ]
  196. all_matches = []
  197. for pattern in patterns:
  198. matches = re.findall(pattern, content)
  199. all_matches.extend(matches)
  200. print(f"找到 {len(all_matches)} 个图片链接")
  201. if not all_matches:
  202. print("未找到图片链接")
  203. return 0, 0
  204. # 替换每个图片URL
  205. new_content = content
  206. for url in all_matches:
  207. original_url = url
  208. # 跳过已经是新路径的URL
  209. if url.startswith(new_base_path):
  210. print(f"跳过(已经是新路径): {url}")
  211. continue
  212. # 查找folder_name在URL中的位置
  213. folder_index = url.find(folder_name)
  214. if folder_index != -1:
  215. # 提取folderName之后的部分
  216. path_after_folder = url[folder_index:]
  217. # 将反斜杠替换为正斜杠
  218. path_after_folder = path_after_folder.replace('\\', '/')
  219. # 构建新的URL
  220. new_url = f"{new_base_path}{path_after_folder}"
  221. # 替换原URL
  222. new_content = new_content.replace(original_url, new_url).replace(f"/{folder_name}/",f"/{extract_dir_name}/")
  223. # 将修改后的内容写回文件
  224. with open(file_path, 'w', encoding='utf-8') as f:
  225. f.write(new_content)
  226. except FileNotFoundError:
  227. print(f"错误: 文件 '{file_path}' 不存在")
  228. except Exception as e:
  229. print(f"处理文件时出错: {e}")
  230. @app.route('/upload_md_zip', methods=['POST'])
  231. def handle_md_zip_upload():
  232. """
  233. 处理MD ZIP文件上传的简化路由
  234. """
  235. result = upload_and_process_md_zip()
  236. return result
  237. # ============ 原有的文件上传函数 ============
  238. def uploadimage(f):
  239. if not (f and allowed_file_img(f.filename)):
  240. return "False"
  241. digest = md5(f.filename.encode("utf-8")).hexdigest()
  242. suffix = Path(f.filename).suffix
  243. images_name = datetime.now().strftime('%Y%m%d%H%M%S') + f'{digest}{suffix}'
  244. file_path = os.path.join(app.config['UPLOAD_FOLDER_IMG_Note'], images_name)
  245. f.save(file_path)
  246. return file_path
  247. def uploadvideo(f):
  248. if not (f and allowed_file_video(f.filename)):
  249. return "False"
  250. digest = md5(f.filename.encode("utf-8")).hexdigest()
  251. suffix = Path(f.filename).suffix
  252. images_name = datetime.now().strftime('%Y%m%d%H%M%S') + f'{digest}{suffix}'
  253. file_path = os.path.join(app.config['UPLOAD_FOLDER_VIDEO_Note'], images_name)
  254. f.save(file_path)
  255. file_path = "/" + file_path
  256. return file_path
  257. @app.route('/viewknowledgenote/<knid>', methods=['POST', 'GET'])
  258. def viewKnowledgeNote(knid):
  259. if 'username' in session:
  260. knowledgenote = loadKnowledgeNotewithKTNameByKNID(knid)
  261. return render_template('admin/a_knowledgenote_view.html', knowledgenote=knowledgenote)
  262. else:
  263. flash('查看失败, 请重新登录')
  264. return redirect(url_for('adminlogin'))
  265. @app.route('/delknowledgenote/<knid>', methods=['POST'])
  266. def delKnowledgeNote(knid):
  267. i, msg = del_KnowledgeNote(knid)
  268. if i > 0:
  269. flash('笔记信息删除成功!')
  270. else:
  271. flash('笔记信息删除失败!%s' % str(msg))
  272. return redirect(url_for('knowledgenotemanage'))