From aa0c4fb8419ee4ee86f1e7d3a7c5b051ebb95116 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 29 Nov 2025 17:26:47 +0800 Subject: [PATCH] =?UTF-8?q?improve:Dlass=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化上传体验 --- Ink Canvas/Helpers/DlassNoteUploader.cs | 247 ++++++++++++++++++++++++ Ink Canvas/MainWindow.xaml.cs | 3 + 2 files changed, 250 insertions(+) diff --git a/Ink Canvas/Helpers/DlassNoteUploader.cs b/Ink Canvas/Helpers/DlassNoteUploader.cs index 04f7a8b6..f1374eaa 100644 --- a/Ink Canvas/Helpers/DlassNoteUploader.cs +++ b/Ink Canvas/Helpers/DlassNoteUploader.cs @@ -19,6 +19,22 @@ namespace Ink_Canvas.Helpers private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA"; private const int BATCH_SIZE = 10; // 批量上传大小 private const int MAX_RETRY_COUNT = 3; // 最大重试次数 + private const string QUEUE_FILE_NAME = "DlassUploadQueue.json"; + + /// + /// 上传队列项 + /// + private class UploadQueueItemData + { + [JsonProperty("file_path")] + public string FilePath { get; set; } + + [JsonProperty("retry_count")] + public int RetryCount { get; set; } + + [JsonProperty("added_time")] + public DateTime AddedTime { get; set; } + } /// /// 上传队列项 @@ -39,6 +55,205 @@ namespace Ink_Canvas.Helpers /// private static readonly SemaphoreSlim _queueProcessingLock = new SemaphoreSlim(1, 1); + /// + /// 队列保存锁,防止并发保存 + /// + private static readonly SemaphoreSlim _queueSaveLock = new SemaphoreSlim(1, 1); + + /// + /// 是否已初始化队列 + /// + private static bool _isQueueInitialized = false; + + /// + /// 获取队列文件路径 + /// + private static string GetQueueFilePath() + { + var configsDir = Path.Combine(App.RootPath, "Configs"); + if (!Directory.Exists(configsDir)) + { + Directory.CreateDirectory(configsDir); + } + return Path.Combine(configsDir, QUEUE_FILE_NAME); + } + + /// + /// 初始化上传队列 + /// + public static void InitializeQueue() + { + if (_isQueueInitialized) + { + return; + } + + try + { + var queueFilePath = GetQueueFilePath(); + if (!File.Exists(queueFilePath)) + { + _isQueueInitialized = true; + return; + } + + var jsonContent = File.ReadAllText(queueFilePath); + if (string.IsNullOrWhiteSpace(jsonContent)) + { + _isQueueInitialized = true; + return; + } + + var queueData = JsonConvert.DeserializeObject>(jsonContent); + if (queueData == null || queueData.Count == 0) + { + _isQueueInitialized = true; + return; + } + + int restoredCount = 0; + int skippedCount = 0; + + foreach (var item in queueData) + { + // 验证文件是否存在 + if (!File.Exists(item.FilePath)) + { + skippedCount++; + continue; + } + + // 验证文件格式和大小 + var fileExtension = Path.GetExtension(item.FilePath).ToLower(); + if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".zip") + { + skippedCount++; + continue; + } + + try + { + var fileInfo = new FileInfo(item.FilePath); + long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024; + if (fileInfo.Length > maxSize) + { + skippedCount++; + continue; + } + } + catch + { + skippedCount++; + continue; + } + + // 恢复队列项 + _uploadQueue.Enqueue(new UploadQueueItem + { + FilePath = item.FilePath, + RetryCount = item.RetryCount + }); + restoredCount++; + } + + _isQueueInitialized = true; + + if (restoredCount > 0) + { + LogHelper.WriteLogToFile($"已恢复上传队列:{restoredCount}个文件,跳过{skippedCount}个无效文件", LogHelper.LogType.Event); + // 如果恢复了队列,触发处理 + _ = ProcessUploadQueueAsync(); + } + else if (skippedCount > 0) + { + LogHelper.WriteLogToFile($"队列恢复完成:跳过{skippedCount}个无效文件", LogHelper.LogType.Event); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"恢复上传队列时出错: {ex.Message}", LogHelper.LogType.Error); + _isQueueInitialized = true; // 即使出错也标记为已初始化,避免重复尝试 + } + } + + /// + /// 保存队列到文件 + /// + private static async Task SaveQueueToFileAsync() + { + if (!await _queueSaveLock.WaitAsync(1000)) // 最多等待1秒 + { + return; // 如果无法获取锁,跳过保存(避免阻塞) + } + + try + { + var queueData = new List(); + + // 将队列转换为可序列化的格式 + foreach (var item in _uploadQueue) + { + queueData.Add(new UploadQueueItemData + { + FilePath = item.FilePath, + RetryCount = item.RetryCount, + AddedTime = DateTime.Now + }); + } + + var queueFilePath = GetQueueFilePath(); + + // 如果队列为空,清空文件 + if (queueData.Count == 0) + { + ClearQueueFile(); + return; + } + + var jsonContent = JsonConvert.SerializeObject(queueData, Formatting.Indented); + + // 使用临时文件写入,然后替换,确保原子性 + var tempFilePath = queueFilePath + ".tmp"; + File.WriteAllText(tempFilePath, jsonContent); + + // 如果原文件存在,先删除 + if (File.Exists(queueFilePath)) + { + File.Delete(queueFilePath); + } + + // 重命名临时文件 + File.Move(tempFilePath, queueFilePath); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存上传队列时出错: {ex.Message}", LogHelper.LogType.Error); + } + finally + { + _queueSaveLock.Release(); + } + } + + /// + /// 清空队列文件 + /// + private static void ClearQueueFile() + { + try + { + var queueFilePath = GetQueueFilePath(); + if (File.Exists(queueFilePath)) + { + File.WriteAllText(queueFilePath, "[]"); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"清空队列文件时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + /// /// 上传笔记响应模型 /// @@ -172,6 +387,9 @@ namespace Ink_Canvas.Helpers RetryCount = retryCount }); + // 异步保存队列到文件 + _ = Task.Run(async () => await SaveQueueToFileAsync()); + // 如果队列达到批量大小,触发批量上传 if (_uploadQueue.Count >= BATCH_SIZE) { @@ -220,6 +438,11 @@ namespace Ink_Canvas.Helpers if (string.IsNullOrEmpty(selectedClassName)) { LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error); + // 将文件重新加入队列 + foreach (var item in filesToUpload) + { + EnqueueFile(item.FilePath, item.RetryCount); + } return; } @@ -227,6 +450,11 @@ namespace Ink_Canvas.Helpers if (string.IsNullOrEmpty(userToken)) { LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error); + // 将文件重新加入队列 + foreach (var item in filesToUpload) + { + EnqueueFile(item.FilePath, item.RetryCount); + } return; } @@ -247,6 +475,11 @@ namespace Ink_Canvas.Helpers if (authResult == null || !authResult.Success || authResult.Whiteboards == null) { LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error); + // 将文件重新加入队列 + foreach (var item in filesToUpload) + { + EnqueueFile(item.FilePath, item.RetryCount); + } return; } @@ -256,6 +489,11 @@ namespace Ink_Canvas.Helpers if (sharedWhiteboard == null || string.IsNullOrEmpty(sharedWhiteboard.BoardId) || string.IsNullOrEmpty(sharedWhiteboard.SecretKey)) { LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error); + // 将文件重新加入队列 + foreach (var item in filesToUpload) + { + EnqueueFile(item.FilePath, item.RetryCount); + } return; } } @@ -263,6 +501,11 @@ namespace Ink_Canvas.Helpers catch (Exception ex) { LogHelper.WriteLogToFile($"批量上传获取白板信息时出错: {ex.Message}", LogHelper.LogType.Error); + // 将文件重新加入队列 + foreach (var item in filesToUpload) + { + EnqueueFile(item.FilePath, item.RetryCount); + } return; } @@ -318,6 +561,9 @@ namespace Ink_Canvas.Helpers }); await Task.WhenAll(uploadTasks); + // 上传完成后保存队列状态 + await SaveQueueToFileAsync(); + // 如果队列达到批量大小,继续处理 if (_uploadQueue.Count >= BATCH_SIZE) { @@ -509,3 +755,4 @@ namespace Ink_Canvas.Helpers } } + diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index 1b0e8887..13e4676d 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -470,6 +470,9 @@ namespace Ink_Canvas LoadSettings(true); AutoBackupManager.Initialize(Settings); + // 初始化Dlass上传队列(恢复上次的上传队列) + DlassNoteUploader.InitializeQueue(); + // 检查保存路径是否可用,不可用则修正 try {