Просмотр исходного кода

1,自测试后整体优化新增代码,使其在慢速网络时UI交互体验等更好; 2,将WEBUI的调试日志级别由DEBUG降级为TRACE,不干扰主功能的DEBUG日志

niujiuru 1 день назад
Родитель
Сommit
c65da54bc0

+ 50 - 22
web-ui.service/page6_handler.go

@@ -193,37 +193,65 @@ func uploadUserAppPkg(w http.ResponseWriter, r *http.Request) {
 
 	r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
 
-	err := r.ParseMultipartForm(maxUploadSize)
+	mr, err := r.MultipartReader()
 	if err != nil {
-		http.Error(w, "解析上传文件失败: "+err.Error(), http.StatusBadRequest)
+		http.Error(w, "解析上传请求失败: "+err.Error(), http.StatusBadRequest)
 		return
 	}
 
-	file, header, err := r.FormFile("file")
-	if err != nil {
-		http.Error(w, "获取上传文件失败: "+err.Error(), http.StatusBadRequest)
-		return
-	}
-	defer file.Close()
+	for {
+		part, err := mr.NextPart()
+		if err == io.EOF {
+			break
+		}
 
-	if !strings.HasSuffix(strings.ToLower(header.Filename), ".tar.gz") {
-		http.Error(w, "只允许上传 tar.gz 类型的文件", http.StatusBadRequest)
-		return
-	}
+		if err != nil {
+			http.Error(w, "读取上传数据失败: "+err.Error(), http.StatusBadRequest)
+			return
+		}
 
-	path := filepath.Join("/tmp", filepath.Base(header.Filename))
+		if part.FormName() != "file" {
+			part.Close()
+			continue
+		}
 
-	err = saveUploadFile(file, path)
-	if err != nil {
-		http.Error(w, "保存上传文件失败: "+err.Error(), http.StatusInternalServerError)
-		return
-	}
+		filename := filepath.Base(part.FileName())
+		if filename == "" || filename == "." {
+			part.Close()
+			http.Error(w, "文件名为空", http.StatusBadRequest)
+			return
+		}
 
-	baseapp.Logger.Infof(
-		"[上传文件成功] 上传文件名: %s, 上传文件大小: %d 字节, 本地存储路径: %s",
-		header.Filename, header.Size, path)
+		if !strings.HasSuffix(strings.ToLower(filename), ".tar.gz") {
+			part.Close()
+			http.Error(w, "只允许上传 tar.gz 类型的文件", http.StatusBadRequest)
+			return
+		}
+
+		path := filepath.Join("/tmp", filename)
+
+		err = saveUploadFile(part, path)
+		part.Close()
+
+		if err != nil {
+			http.Error(w, "保存上传文件失败: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
 
-	w.WriteHeader(http.StatusOK)
+		size := int64(0)
+		if stat, err := os.Stat(path); err == nil {
+			size = stat.Size()
+		}
+
+		baseapp.Logger.Infof(
+			"[上传文件成功] 上传文件名: %s, 上传文件大小: %d 字节, 本地存储路径: %s",
+			filename, size, path)
+
+		break
+	}
+
+	w.Header().Set("Content-Type", "application/json; charset=utf-8")
+	json.NewEncoder(w).Encode(map[string]string{"result": "success"})
 }
 
 func saveUploadFile(src io.Reader, path string) error {

+ 2 - 2
web-ui.service/page8_handler.go

@@ -39,7 +39,7 @@ func sshWSHandler(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if true {
-		baseapp.Logger.Debugf("[SSH终端打开]")
+		baseapp.Logger.Tracef("[SSH终端打开]")
 	}
 
 	defer func() {
@@ -47,7 +47,7 @@ func sshWSHandler(w http.ResponseWriter, r *http.Request) {
 		if cmd.Process != nil {
 			syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
 		}
-		baseapp.Logger.Debugf("[SSH终端关闭]")
+		baseapp.Logger.Tracef("[SSH终端关闭]")
 	}()
 
 	pty.Setsize(ptmx, &pty.Winsize{Cols: 120, Rows: 40})

+ 31 - 13
web-ui.service/web/static/js/page5.js

@@ -227,10 +227,27 @@ async function cameraTakePhoto(filename) {
   return await r.json();
 }
 
-function setImageSrc(id, url)
+let imageLoadSeq = 0;
+function setImageSrc(id, url, showLoadStatus = true)
 {
   const img = document.getElementById(id);
-  if (img) img.src = url;
+  if (!img) return;
+
+  const seq = ++imageLoadSeq;
+  const start = Date.now();
+
+  img.onload = function() {
+    if (seq !== imageLoadSeq || !showLoadStatus) return;
+    setText("cvar1", "图片加载完成, 耗时:" + (Date.now() - start) + "ms", "page5");
+  };
+
+  img.onerror = function() {
+    if (seq !== imageLoadSeq || !showLoadStatus) return;
+    setText("cvar1", "图片加载失败, 耗时:" + (Date.now() - start) + "ms", "page5");
+  }
+
+  const separator = url.includes("?") ? "&" : "?";
+  img.src = url + separator + "t=" + Date.now();
 }
 
 async function takeOnePhoto() {
@@ -258,15 +275,15 @@ async function takeOnePhoto() {
     const data = await cameraTakePhoto(filename);
     if (!data.ok) throw new Error("take photo failed");
 
-    cameraCurrentFile = filename;
-    setImageSrc("camera-image", "/api/camera/getImage?file=" + encodeURIComponent(filename) + "&t=" + Date.now());
-
-    setText("cvar1", "拍照完成", "page5");
+    setText("cvar1", "预览中, 请稍后...", "page5");
     setText("cvar2", "1", "page5");
     setText("cvar3", data.var1, "page5");
     setText("cvar4", data.var2, "page5");
     setText("cvar5", data.var3, "page5");
     setText("cvar6", data.var4 + "x" + data.var5, "page5");
+
+    cameraCurrentFile = filename;
+    setImageSrc("camera-image", "/api/camera/getImage?file=" + encodeURIComponent(cameraCurrentFile), true);
   } catch(e) {
     console.error("takeOnePhoto failed:", e);
     setText("cvar1", "拍照失败", "page5");
@@ -327,15 +344,16 @@ async function cameraPreviewLoop() {
     const data = await cameraTakePhoto(filename);
     if (!data.ok) throw new Error("take photo failed");
 
-    cameraCurrentFile = filename;
+    setText("cvar1", "预览中, 请稍后...", "page5");
     cameraPreviewFrames++;
-    setImageSrc("camera-image", "/api/camera/getImage?file=" + encodeURIComponent(filename) + "&t=" + Date.now());
-    setText("cvar1", "拍照完成", "page5");
     setText("cvar2", String(cameraPreviewFrames), "page5");
     setText("cvar3", data.var1, "page5");
     setText("cvar4", data.var2, "page5");
     setText("cvar5", data.var3, "page5");
     setText("cvar6", data.var4 + "x" + data.var5, "page5");
+
+    cameraCurrentFile = filename;
+    setImageSrc("camera-image", "/api/camera/getImage?file=" + encodeURIComponent(cameraCurrentFile), false);
   } catch(e) {
     console.error("cameraPreviewLoop failed:", e);
     stopCameraPreview();
@@ -346,6 +364,7 @@ async function cameraPreviewLoop() {
     if (cameraPreviewRunning) {
       cameraPreviewTimer = setTimeout(cameraPreviewLoop, 20);
     } else {
+      setText("cvar1", "连拍已停止", "page5");
       setCameraWaitAEDone(true)
       .catch(e => {
         console.error("restore wait AE failed:", e);
@@ -364,7 +383,7 @@ function stopCameraPreview() {
     clearTimeout(cameraPreviewTimer);
     cameraPreviewTimer = null;
   }
-  setText("cvar1", "停止中, 请稍后...", "page5");
+  setText("cvar1", "停止连怕中, 请稍后...", "page5");
 }
 
 function downloadCameraImage() {
@@ -384,11 +403,10 @@ function downloadCameraImage() {
 
 function initPage5() {
   getCameraStatus();
-  if (cameraCurrentFile) {
-    setImageSrc("camera-image", "/api/camera/getImage?file=" + encodeURIComponent(cameraCurrentFile) + "&t=" + Date.now());
-  }
   if (cameraTakingPhoto || cameraPreviewRunning) {
     setText("cvar1", "拍照中, 请稍后...", "page5");
+  } else if (cameraCurrentFile) {
+    setImageSrc("camera-image", "/api/camera/getImage?file=" + encodeURIComponent(cameraCurrentFile), true);
   }
   connectLog("yfkj-camera-capture.service", "text1");
 }

+ 19 - 7
web-ui.service/web/static/js/page6.js

@@ -116,7 +116,7 @@ function restartAppInstallService(event) {
 let appInsStatusTimer = null;
 let userAppOperating = false;
 function getAppInstallStatus() {
-  if (userAppOperating) {
+  if (userAppOperating || userAppUploading) {
     return;
   }
 
@@ -243,6 +243,7 @@ function uploadUserAppPkg(input) {
   }
 
   userAppUploading = true;
+  setBtnStatus("btn1", false, "page6");
 
   let formData = new FormData();
   formData.append("file", file);
@@ -257,7 +258,11 @@ function uploadUserAppPkg(input) {
     }
 
     let percent = Math.floor(e.loaded * 100 / e.total);
-    setText("bvar5", "正在上传 " + file.name + " (" + percent + "%)", "page6");
+    if (percent >= 100) {
+      setText("bvar5", "上传完成,正在处理安装包,请稍候...", "page6");
+    } else {
+      setText("bvar5", "正在上传 " + file.name + " (" + percent + "%)", "page6");
+    }
   };
 
   xhr.onload = async function() {
@@ -265,12 +270,17 @@ function uploadUserAppPkg(input) {
       userAppUploading = false;
       input.value = "";
       setText("bvar5", "上传失败: " + xhr.responseText, "page6");
+      setBtnStatus("btn1", true, "page6");
       return;
     }
 
-    await importUserAppPkg("/tmp/" + file.name);
-    userAppUploading = false;
-    input.value = "";
+    try {
+      await importUserAppPkg("/tmp/" + file.name);
+    } finally {
+      userAppUploading = false;
+      input.value = "";
+      setBtnStatus("btn1", true, "page6");
+    }
   };
 
   xhr.onerror = function() {
@@ -278,6 +288,7 @@ function uploadUserAppPkg(input) {
     input.value = "";
 
     setText("bvar5", "上传失败,请检查网络连接", "page6");
+    setBtnStatus("btn1", true, "page6");
   };
 
   xhr.ontimeout = function() {
@@ -285,6 +296,7 @@ function uploadUserAppPkg(input) {
     input.value = "";
 
     setText("bvar5", "上传超时,请稍后重新尝试", "page6");
+    setBtnStatus("btn1", true, "page6");
   };
 
   xhr.send(formData);
@@ -310,12 +322,12 @@ async function importUserAppPkg(file) {
       throw new Error("返回结果异常");
     }
 
-    userAppOperating = false;
     return true;
   } catch (err) {
     setText("bvar5", "导入过程中发生错误: " + err.message, "page6");
-    userAppOperating = false;
     return false;
+  } finally {
+    userAppOperating = false;
   }
 }
 

+ 7 - 7
web-ui.service/web_handler.go

@@ -202,7 +202,7 @@ func serviceLogHandler(w http.ResponseWriter, r *http.Request) {
 
 	id := time.Now().UnixNano()
 	if true {
-		baseapp.Logger.Debugf("[服务日志流启动] id=%d unit=%s", id, unit)
+		baseapp.Logger.Tracef("[服务日志流启动] id=%d unit=%s", id, unit)
 	}
 
 	defer func() {
@@ -211,7 +211,7 @@ func serviceLogHandler(w http.ResponseWriter, r *http.Request) {
 		}
 		cmd.Wait()
 
-		baseapp.Logger.Debugf("[服务日志流关闭] id=%d unit=%s", id, unit)
+		baseapp.Logger.Tracef("[服务日志流关闭] id=%d unit=%s", id, unit)
 	}()
 
 	lines := make(chan string, 100)
@@ -242,7 +242,7 @@ func serviceLogHandler(w http.ResponseWriter, r *http.Request) {
 }
 
 func runShellAndWrite(w http.ResponseWriter, cmd string) {
-	baseapp.Logger.Debugf("[执行命令] %s", cmd)
+	baseapp.Logger.Tracef("[执行命令] %s", cmd)
 
 	out, err := exec.Command("sh", "-c", cmd).CombinedOutput()
 	if err != nil {
@@ -272,7 +272,7 @@ func callRPCResult(ctx context.Context, port int, method string, params any, res
 	url := fmt.Sprintf("http://127.0.0.1:%d/rpc", port)
 
 	req, _ := json.Marshal(params)
-	baseapp.Logger.Debugf("[接收RPC请求] %s %s params=%s\n", url, method, string(req))
+	baseapp.Logger.Tracef("[接收RPC请求] %s %s params=%s\n", url, method, string(req))
 
 	client, err := jsonrpc2.NewRPCClient(url)
 	if err != nil {
@@ -284,7 +284,7 @@ func callRPCResult(ctx context.Context, port int, method string, params any, res
 		return err
 	}
 
-	baseapp.Logger.Debugf("[发送RPC应答] %s result=%s err=%v\n", method, string(resp.Result), resp.Error)
+	baseapp.Logger.Tracef("[发送RPC应答] %s result=%s err=%v\n", method, string(resp.Result), resp.Error)
 
 	if resp.Error != nil {
 		return fmt.Errorf("%s", resp.Error.Message)
@@ -301,7 +301,7 @@ func callRPCResponse(w http.ResponseWriter, r *http.Request, port int, method st
 	url := fmt.Sprintf("http://127.0.0.1:%d/rpc", port)
 
 	req, _ := json.Marshal(params)
-	baseapp.Logger.Debugf("[接收RPC请求] %s %s params=%s\n", url, method, string(req))
+	baseapp.Logger.Tracef("[接收RPC请求] %s %s params=%s\n", url, method, string(req))
 
 	client, err := jsonrpc2.NewRPCClient(url)
 	if err != nil {
@@ -315,7 +315,7 @@ func callRPCResponse(w http.ResponseWriter, r *http.Request, port int, method st
 		return
 	}
 
-	baseapp.Logger.Debugf("[发送RPC应答] %s result=%s err=%v\n", method, string(resp.Result), resp.Error)
+	baseapp.Logger.Tracef("[发送RPC应答] %s result=%s err=%v\n", method, string(resp.Result), resp.Error)
 
 	if resp.Error != nil {
 		http.Error(w, resp.Error.Message, http.StatusBadRequest)