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
{