import ctypes import logging import os import shutil import site import sys import pythoncom import pywintypes import win32com.client from flask import Flask, jsonify, request, current_app from flask_cors import CORS from win10toast import ToastNotifier from core.wps_handle import is_file_open_in_wps, bring_wps_window_to_front from tools.check import detect_oss_file_changes, detect_local_file_changes from tools.config import get_config, file_type_map from tools.file_manager import get_file_md5 from tools.logger_handle import logger from tools.oss_client import oss_handle from tools.serve_client import ServerClient toaster = ToastNotifier() app = Flask(__name__) class InterceptHandler(logging.Handler): def emit(self, record): # 将标准日志记录转为 loguru try: level = logger.level(record.levelname).name except ValueError: level = record.levelno logger.opt(depth=6, exception=record.exc_info).log(level, record.getMessage()) logging.basicConfig(handlers=[InterceptHandler()], level=logging.INFO) for name in ("flask", "werkzeug", "watchdog"): logging.getLogger(name).handlers = [InterceptHandler()] logging.getLogger(name).propagate = False CORS(app) @logger.catch() def open_file_by_wps(file_path): ext = file_path.split(".")[-1] shutil.rmtree(win32com.client.gencache.GetGeneratePath()) if ext not in file_type_map: return jsonify({'code': 3001, 'msg': '不支持该类型的文件'}) if not os.path.exists(file_path): return jsonify({'code': 3006, 'msg': '文件不存在'}) pythoncom.CoInitialize() try: if ext in ['csv', 'xlsx', 'xls']: # wps = win32com.client.gencache.EnsureDispatch(file_type_map['xlsx'][0]) wps = win32com.client.Dispatch(file_type_map[ext][0]) wps.Visible = True getattr(wps, file_type_map[ext][1]).Open(os.path.abspath(file_path)) else: wps = win32com.client.Dispatch(file_type_map[ext][0]) wps.Visible = True getattr(wps, file_type_map[ext][1]).open(os.path.abspath(file_path)) except pywintypes.com_error as e: logger.exception(e) bring_wps_window_to_front(file_path) pythoncom.CoUninitialize() return jsonify({'code': 1000, 'msg': '操作完成'}) @logger.catch() @app.route('/download_and_open_file', methods=['POST']) def download_and_open_with_wps(): ''' 1000: 操作完成 2001: 检测到文档正在被编辑,无法下载 2002: 检测到远程文件已经更新本地文件也发生改动,终止操作 3001: 不支持该类型的文件 3002: 下载文件失败 :return: ''' file_id = request.get_json()['file_id'] # 判断是否为exe模式执行 file_info = app.config['serve_client'].get_file_info(file_id) if not file_info: return jsonify({'code': 3006, 'msg': '文件不存在'}) local_file = os.path.join(current_app.config['work_path'], file_info['filePath'].replace('/', '_')) # 判断本地是否存在已下载的同名文件 if not os.path.exists(local_file) or not os.path.exists(local_file + '.metadata.json'): app.config['serve_client'].download_file(file_info, current_app.config['work_path']) return open_file_by_wps(local_file) # 判断本地文件是否被打开 if is_file_open_in_wps(local_file): bring_wps_window_to_front(local_file) return jsonify({'code': 1000, 'msg': '检测到文件已被打开'}) # 判断本地文件和线上文件是否一致 local_file_metadata = oss_handle.load_metadata(local_file) if local_file_metadata['update_time'] == file_info['updateTime']: return open_file_by_wps(local_file) if local_file_metadata['md5'] == app.config['serve_client'].get_file_md5(local_file): app.config['serve_client'].download_file(file_info, current_app.config['work_path']) return open_file_by_wps(local_file) return jsonify({'code': 2002, 'msg': '检测到远程文件已经更新本地文件也发生改动,终止操作'}) @logger.catch() @app.route('/upload_local_file', methods=['POST']) def upload_local_file(): file_name = request.get_json()['file_name'] if is_file_open_in_wps(os.path.join(current_app.config['work_path'], file_name)): return jsonify({'code': 3004, 'msg': '操作失败,文件已被打开无法操作'}) metadata = app.config['serve_client'].load_metadata(os.path.join(current_app.config['work_path'], file_name)) code = app.config['serve_client'].upload_file(os.path.join(current_app.config['work_path'], file_name)) if not code: return jsonify({'code': 3003, 'msg': '操作失败,上传文件失败'}) try: os.remove(os.path.join(current_app.config['work_path'], file_name)) os.remove(os.path.join(current_app.config['work_path'], file_name + '.metadata')) file_info = app.config['serve_client'].get_file_info(metadata['file_id']) if not file_info: return jsonify({'code': 3006, 'msg': '文件不存在'}) app.config['serve_client'].download_file(file_info, current_app.config['work_path']) except Exception as e: logger.exception(e) return jsonify({'code': 1000, 'msg': '文件上传成功,本地文件清除失败'}) return jsonify({'code': 1000, 'msg': '操作成功'}) @logger.catch() @app.route('/update_local_file', methods=['POST']) def update_local_file(): file_name = request.get_json()['file_name'] if is_file_open_in_wps(os.path.join(current_app.config['work_path'], file_name)): return jsonify({'code': 3004, 'msg': '操作失败,文件已被打开无法操作'}) try: metadata = oss_handle.load_metadata(os.path.join(current_app.config['work_path'], file_name)) os.remove(os.path.join(current_app.config['work_path'], file_name)) os.remove(os.path.join(current_app.config['work_path'], file_name + '.metadata')) except Exception as e: logger.exception(e) return jsonify({'code': 3005, 'msg': '操作失败,无权限操作本地文件'}) try: file_info = app.config['serve_client'].get_file_info(metadata['file_id']) if not app.config['serve_client'].download_file(file_info, current_app.config['work_path']): return jsonify({'code': 3005, 'msg': '文件下载失败'}) except Exception as e: logger.exception(e) return jsonify({'code': 3005, 'msg': '文件下载失败'}) return jsonify({'code': 1000, 'msg': '操作成功'}) @logger.catch() @app.route('/file_state_list', methods=['GET']) def file_state_list(): ''' state : 0 正常, 1: 云端有更新 2: 本地有变动且无冲突可以上传 3: 本地文件和云端文件有冲突 :return: ''' file_info_list = [] for file_name in os.listdir(current_app.config['work_path']): suffer = file_name.split('.')[-1] if suffer not in file_type_map: continue if not os.path.exists(os.path.join(current_app.config['work_path'], file_name + '.metadata')): continue metadata = oss_handle.load_metadata(os.path.join(current_app.config['work_path'], file_name)) file_info = { 'file_name': file_name, 'show_name': metadata['file_name'], 'cloud_update_time': metadata['update_time'], 'source_md5': metadata['md5'], 'state': 0 } cloud_file_info = current_app.config['serve_client'].get_file_info(metadata['file_id']) if cloud_file_info == 2: file_info['state'] = 4 file_info_list.append(file_info) continue if not cloud_file_info: file_info_list.append(file_info) continue if cloud_file_info['updateTime'] != metadata['update_time']: file_info['state'] = 1 try: file_md5 = get_file_md5(os.path.join(current_app.config['work_path'], file_name)) if file_md5 != metadata['md5']: if file_info['state'] == 1: file_info['state'] = 3 else: file_info['state'] = 2 except Exception as e: logger.exception(e) file_info_list.append(file_info) return jsonify({'code': 1000, 'msg': '操作成功', 'data': file_info_list}) @logger.catch() @app.route('/remove_file', methods=['POST']) def remove_file(): file_name = request.get_json()['file_name'] file_path = os.path.join(current_app.config['work_path'], file_name) if not os.path.exists(file_path): return jsonify({'code': '3006', 'msg': '文件不存在'}) try: os.remove(file_path) os.remove(f'{file_path}.metadata') except Exception as e: logger.exception(e) return jsonify({'code': 3005, 'msg': '操作失败,无权限操作本地文件'}) return jsonify({'code': 1000, 'msg': '操作成功'}) @logger.catch() @app.route('/open_file', methods=['POST']) def open_file(): file_name = request.get_json()['file_name'] file_path = os.path.join(current_app.config['work_path'], file_name) if not os.path.exists(file_path): return jsonify({'code': '3006', 'msg': '文件不存在'}) if is_file_open_in_wps(file_path): bring_wps_window_to_front(file_path) return jsonify({'code': 1000, 'msg': '检测到文件已被打开'}) return open_file_by_wps(file_path) def start_flask(serve_client, work_path): app.config['serve_client'] = serve_client app.config['work_path'] = work_path # app.logger.handlers.clear() # app.logger.addHandler(InterceptHandler()) app.run(host='0.0.0.0', port=5855)