2025-11-02 10:11:15 +08:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using System;
|
2025-11-02 10:30:36 +08:00
|
|
|
|
using System.Collections.Concurrent;
|
2025-11-02 10:11:15 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
2025-11-02 10:30:36 +08:00
|
|
|
|
using System.Threading;
|
2025-11-02 10:11:15 +08:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Ink_Canvas.Helpers
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Dlass笔记自动上传辅助类
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class DlassNoteUploader
|
|
|
|
|
|
{
|
|
|
|
|
|
private const string APP_ID = "app_WkjocWqsrVY7T6zQV2CfiA";
|
|
|
|
|
|
private const string APP_SECRET = "o7dx5b5ASGUMcM72PCpmRQYAhSijqaOVHoGyBK0IxbA";
|
2025-11-02 10:30:36 +08:00
|
|
|
|
private const int BATCH_SIZE = 10; // 批量上传大小
|
2025-11-02 10:46:16 +08:00
|
|
|
|
private const int MAX_RETRY_COUNT = 3; // 最大重试次数
|
2025-12-20 13:56:46 +08:00
|
|
|
|
private const string QUEUE_FILE_NAME = "DlassUploadQueue.json";
|
2025-11-29 17:26:47 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 上传队列项
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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; }
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-11-02 10:46:16 +08:00
|
|
|
|
/// 上传队列项
|
2025-11-02 10:30:36 +08:00
|
|
|
|
/// </summary>
|
2025-11-02 10:46:16 +08:00
|
|
|
|
private class UploadQueueItem
|
|
|
|
|
|
{
|
|
|
|
|
|
public string FilePath { get; set; }
|
|
|
|
|
|
public int RetryCount { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 上传队列
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static readonly ConcurrentQueue<UploadQueueItem> _uploadQueue = new ConcurrentQueue<UploadQueueItem>();
|
2025-11-02 10:30:36 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 队列处理锁,防止并发处理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static readonly SemaphoreSlim _queueProcessingLock = new SemaphoreSlim(1, 1);
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 队列保存锁,防止并发保存
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static readonly SemaphoreSlim _queueSaveLock = new SemaphoreSlim(1, 1);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否已初始化队列
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static bool _isQueueInitialized = false;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取队列文件路径
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化上传队列
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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<List<UploadQueueItemData>>(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();
|
2026-01-01 18:15:33 +08:00
|
|
|
|
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".xml" && fileExtension != ".zip")
|
2025-11-29 17:26:47 +08:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
// 如果恢复了队列,触发处理
|
2026-02-12 21:52:28 +08:00
|
|
|
|
_ = Task.Run(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await ProcessUploadQueueAsync().ConfigureAwait(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"恢复上传队列后处理时出错: {ex}", LogHelper.LogType.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-11-29 17:26:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
else if (skippedCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"队列恢复完成:跳过{skippedCount}个无效文件", LogHelper.LogType.Event);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"恢复上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
|
|
|
|
_isQueueInitialized = true; // 即使出错也标记为已初始化,避免重复尝试
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 保存队列到文件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static async Task SaveQueueToFileAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!await _queueSaveLock.WaitAsync(1000)) // 最多等待1秒
|
|
|
|
|
|
{
|
|
|
|
|
|
return; // 如果无法获取锁,跳过保存(避免阻塞)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var queueData = new List<UploadQueueItemData>();
|
2025-12-20 13:56:46 +08:00
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 将队列转换为可序列化的格式
|
|
|
|
|
|
foreach (var item in _uploadQueue)
|
|
|
|
|
|
{
|
|
|
|
|
|
queueData.Add(new UploadQueueItemData
|
|
|
|
|
|
{
|
|
|
|
|
|
FilePath = item.FilePath,
|
|
|
|
|
|
RetryCount = item.RetryCount,
|
|
|
|
|
|
AddedTime = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var queueFilePath = GetQueueFilePath();
|
2025-12-20 13:56:46 +08:00
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 如果队列为空,清空文件
|
|
|
|
|
|
if (queueData.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ClearQueueFile();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var jsonContent = JsonConvert.SerializeObject(queueData, Formatting.Indented);
|
2025-12-20 13:56:46 +08:00
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 使用临时文件写入,然后替换,确保原子性
|
|
|
|
|
|
var tempFilePath = queueFilePath + ".tmp";
|
|
|
|
|
|
File.WriteAllText(tempFilePath, jsonContent);
|
2025-12-20 13:56:46 +08:00
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 如果原文件存在,先删除
|
|
|
|
|
|
if (File.Exists(queueFilePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
File.Delete(queueFilePath);
|
|
|
|
|
|
}
|
2025-12-20 13:56:46 +08:00
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 重命名临时文件
|
|
|
|
|
|
File.Move(tempFilePath, queueFilePath);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"保存上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_queueSaveLock.Release();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 清空队列文件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:11:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 上传笔记响应模型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class UploadNoteResponse
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("success")]
|
|
|
|
|
|
public bool Success { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("message")]
|
|
|
|
|
|
public string Message { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("note_id")]
|
|
|
|
|
|
public int? NoteId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("filename")]
|
|
|
|
|
|
public string Filename { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("file_path")]
|
|
|
|
|
|
public string FilePath { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("file_url")]
|
|
|
|
|
|
public string FileUrl { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 白板信息模型(用于查找白板)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private class WhiteboardInfo
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("id")]
|
|
|
|
|
|
public int Id { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("name")]
|
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("board_id")]
|
|
|
|
|
|
public string BoardId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("secret_key")]
|
|
|
|
|
|
public string SecretKey { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("class_name")]
|
|
|
|
|
|
public string ClassName { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("class_id")]
|
|
|
|
|
|
public int ClassId { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 认证响应模型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private class AuthWithTokenResponse
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("success")]
|
|
|
|
|
|
public bool Success { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("whiteboards")]
|
|
|
|
|
|
public List<WhiteboardInfo> Whiteboards { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:16:31 +08:00
|
|
|
|
/// <summary>
|
2025-12-31 16:40:47 +08:00
|
|
|
|
/// 异步上传笔记文件到Dlass(支持PNG、ICSTK、XML和ZIP格式)
|
2025-11-02 10:16:31 +08:00
|
|
|
|
/// </summary>
|
2025-12-31 16:40:47 +08:00
|
|
|
|
/// <param name="filePath">文件路径(支持PNG、ICSTK、XML和ZIP)</param>
|
2025-11-02 10:30:36 +08:00
|
|
|
|
/// <returns>是否成功加入队列(不等待实际上传完成)</returns>
|
2025-11-02 10:16:31 +08:00
|
|
|
|
public static async Task<bool> UploadNoteFileAsync(string filePath)
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否启用自动上传
|
|
|
|
|
|
if (MainWindow.Settings?.Dlass?.IsAutoUploadNotes != true)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
// 基本验证
|
|
|
|
|
|
if (!File.Exists(filePath))
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
2025-11-02 10:30:36 +08:00
|
|
|
|
LogHelper.WriteLogToFile($"上传失败:文件不存在 - {filePath}", LogHelper.LogType.Error);
|
2025-11-02 10:11:15 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
var fileExtension = Path.GetExtension(filePath).ToLower();
|
2025-12-31 16:40:47 +08:00
|
|
|
|
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".xml" && fileExtension != ".zip")
|
2025-11-02 10:16:31 +08:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
var fileInfo = new FileInfo(filePath);
|
2025-11-08 21:01:50 +08:00
|
|
|
|
long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024;
|
|
|
|
|
|
if (fileInfo.Length > maxSize)
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
2025-11-08 21:01:50 +08:00
|
|
|
|
LogHelper.WriteLogToFile($"上传失败:文件过大({fileInfo.Length / 1024 / 1024}MB),超过{maxSize / 1024 / 1024}MB限制", LogHelper.LogType.Error);
|
2025-11-02 10:11:15 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
// 获取上传延迟时间(分钟)
|
|
|
|
|
|
var delayMinutes = MainWindow.Settings?.Dlass?.AutoUploadDelayMinutes ?? 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果设置了延迟时间,在后台任务中等待后再加入队列
|
|
|
|
|
|
if (delayMinutes > 0)
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
2025-11-02 10:30:36 +08:00
|
|
|
|
_ = Task.Run(async () =>
|
|
|
|
|
|
{
|
2026-02-12 21:52:28 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(TimeSpan.FromMinutes(delayMinutes)).ConfigureAwait(false);
|
2026-02-13 09:17:18 +08:00
|
|
|
|
if (MainWindow.Settings?.Dlass?.IsAutoUploadNotes != true)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"延迟结束后自动上传已关闭,跳过入队: {filePath}", LogHelper.LogType.Event);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!File.Exists(filePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"延迟结束后文件已不存在,跳过入队: {filePath}", LogHelper.LogType.Event);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-02-12 21:52:28 +08:00
|
|
|
|
EnqueueFile(filePath);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"延迟加入上传队列时出错: {ex}", LogHelper.LogType.Error);
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
});
|
2025-11-02 10:11:15 +08:00
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
EnqueueFile(filePath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"加入上传队列时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 将文件加入上传队列
|
|
|
|
|
|
/// </summary>
|
2025-11-02 10:46:16 +08:00
|
|
|
|
private static void EnqueueFile(string filePath, int retryCount = 0)
|
2025-11-02 10:30:36 +08:00
|
|
|
|
{
|
2025-11-02 10:46:16 +08:00
|
|
|
|
_uploadQueue.Enqueue(new UploadQueueItem
|
|
|
|
|
|
{
|
|
|
|
|
|
FilePath = filePath,
|
|
|
|
|
|
RetryCount = retryCount
|
|
|
|
|
|
});
|
2025-11-02 10:30:36 +08:00
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 异步保存队列到文件
|
2026-02-12 21:52:28 +08:00
|
|
|
|
_ = Task.Run(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await SaveQueueToFileAsync().ConfigureAwait(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"保存上传队列时出错(后台任务): {ex}", LogHelper.LogType.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-11-29 17:26:47 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
// 如果队列达到批量大小,触发批量上传
|
|
|
|
|
|
if (_uploadQueue.Count >= BATCH_SIZE)
|
|
|
|
|
|
{
|
|
|
|
|
|
_ = ProcessUploadQueueAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理上传队列,批量上传文件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static async Task ProcessUploadQueueAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 使用信号量防止并发处理
|
|
|
|
|
|
if (!await _queueProcessingLock.WaitAsync(0))
|
|
|
|
|
|
{
|
|
|
|
|
|
return; // 已有处理任务在运行
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-11-02 10:46:16 +08:00
|
|
|
|
var filesToUpload = new List<UploadQueueItem>();
|
2025-11-02 10:30:36 +08:00
|
|
|
|
|
|
|
|
|
|
// 从队列中取出最多BATCH_SIZE个文件
|
2025-11-02 10:46:16 +08:00
|
|
|
|
while (filesToUpload.Count < BATCH_SIZE && _uploadQueue.TryDequeue(out UploadQueueItem item))
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
2025-11-02 10:46:16 +08:00
|
|
|
|
// 再次检查文件是否存在
|
|
|
|
|
|
if (File.Exists(item.FilePath))
|
2025-11-02 10:30:36 +08:00
|
|
|
|
{
|
2025-11-02 10:46:16 +08:00
|
|
|
|
filesToUpload.Add(item);
|
2025-11-02 10:30:36 +08:00
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
if (filesToUpload.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
// 获取共享的白板信息(同一批次的所有文件共享认证信息)
|
|
|
|
|
|
WhiteboardInfo sharedWhiteboard = null;
|
|
|
|
|
|
string apiBaseUrl = null;
|
|
|
|
|
|
string userToken = null;
|
2025-12-20 13:56:46 +08:00
|
|
|
|
|
2025-11-02 10:11:15 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-11-02 10:30:36 +08:00
|
|
|
|
var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName;
|
|
|
|
|
|
if (string.IsNullOrEmpty(selectedClassName))
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error);
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 将文件重新加入队列
|
|
|
|
|
|
foreach (var item in filesToUpload)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnqueueFile(item.FilePath, item.RetryCount);
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
userToken = MainWindow.Settings?.Dlass?.UserToken;
|
|
|
|
|
|
if (string.IsNullOrEmpty(userToken))
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
2025-11-02 10:30:36 +08:00
|
|
|
|
LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 将文件重新加入队列
|
|
|
|
|
|
foreach (var item in filesToUpload)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnqueueFile(item.FilePath, item.RetryCount);
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
apiBaseUrl = MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
// 获取白板信息(只获取一次,所有文件共享)
|
|
|
|
|
|
using (var apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken))
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
2025-11-02 10:30:36 +08:00
|
|
|
|
var authData = new
|
|
|
|
|
|
{
|
|
|
|
|
|
app_id = APP_ID,
|
|
|
|
|
|
app_secret = APP_SECRET,
|
|
|
|
|
|
user_token = userToken
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var authResult = await apiClient.PostAsync<AuthWithTokenResponse>("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false);
|
|
|
|
|
|
|
|
|
|
|
|
if (authResult == null || !authResult.Success || authResult.Whiteboards == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error);
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 将文件重新加入队列
|
|
|
|
|
|
foreach (var item in filesToUpload)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnqueueFile(item.FilePath, item.RetryCount);
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sharedWhiteboard = authResult.Whiteboards
|
|
|
|
|
|
.FirstOrDefault(w => !string.IsNullOrEmpty(w.ClassName) && w.ClassName == selectedClassName);
|
|
|
|
|
|
|
|
|
|
|
|
if (sharedWhiteboard == null || string.IsNullOrEmpty(sharedWhiteboard.BoardId) || string.IsNullOrEmpty(sharedWhiteboard.SecretKey))
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error);
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 将文件重新加入队列
|
|
|
|
|
|
foreach (var item in filesToUpload)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnqueueFile(item.FilePath, item.RetryCount);
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"批量上传获取白板信息时出错: {ex.Message}", LogHelper.LogType.Error);
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 将文件重新加入队列
|
|
|
|
|
|
foreach (var item in filesToUpload)
|
|
|
|
|
|
{
|
|
|
|
|
|
EnqueueFile(item.FilePath, item.RetryCount);
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:46:16 +08:00
|
|
|
|
// 并发上传所有文件(共享白板信息),并处理失败重试
|
|
|
|
|
|
var uploadTasks = filesToUpload.Select(async item =>
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var success = await UploadFileInternalAsync(item.FilePath, sharedWhiteboard, apiBaseUrl, userToken);
|
|
|
|
|
|
if (!success)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否是可重试的错误
|
|
|
|
|
|
if (IsRetryableError(item.FilePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查重试次数
|
|
|
|
|
|
if (item.RetryCount < MAX_RETRY_COUNT)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"上传失败,将重试 ({item.RetryCount + 1}/{MAX_RETRY_COUNT}): {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Event);
|
|
|
|
|
|
EnqueueFile(item.FilePath, item.RetryCount + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"上传失败,已达到最大重试次数: {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return success;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否是可重试的错误(超时、网络错误等)
|
|
|
|
|
|
var errorMessage = ex.Message.ToLower();
|
2025-12-20 13:56:46 +08:00
|
|
|
|
bool isRetryable = errorMessage.Contains("超时") ||
|
2025-11-02 10:46:16 +08:00
|
|
|
|
errorMessage.Contains("timeout") ||
|
|
|
|
|
|
errorMessage.Contains("网络错误") ||
|
|
|
|
|
|
errorMessage.Contains("network");
|
2025-12-20 13:56:46 +08:00
|
|
|
|
|
2025-11-02 10:46:16 +08:00
|
|
|
|
if (isRetryable && IsRetryableError(item.FilePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查重试次数
|
|
|
|
|
|
if (item.RetryCount < MAX_RETRY_COUNT)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"上传失败({ex.Message}),将重试 ({item.RetryCount + 1}/{MAX_RETRY_COUNT}): {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Event);
|
|
|
|
|
|
EnqueueFile(item.FilePath, item.RetryCount + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"上传失败,已达到最大重试次数: {Path.GetFileName(item.FilePath)}", LogHelper.LogType.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-11-02 10:30:36 +08:00
|
|
|
|
await Task.WhenAll(uploadTasks);
|
|
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
// 上传完成后保存队列状态
|
|
|
|
|
|
await SaveQueueToFileAsync();
|
|
|
|
|
|
|
2025-11-02 10:46:16 +08:00
|
|
|
|
// 如果队列达到批量大小,继续处理
|
2025-11-02 10:30:36 +08:00
|
|
|
|
if (_uploadQueue.Count >= BATCH_SIZE)
|
|
|
|
|
|
{
|
2026-02-12 21:52:28 +08:00
|
|
|
|
_ = Task.Run(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await ProcessUploadQueueAsync().ConfigureAwait(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"继续批量处理上传队列时出错: {ex}", LogHelper.LogType.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-11-02 10:30:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_queueProcessingLock.Release();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 内部上传方法,执行实际上传操作
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="filePath">文件路径</param>
|
|
|
|
|
|
/// <param name="whiteboard">白板信息(如果为null则重新获取)</param>
|
|
|
|
|
|
/// <param name="apiBaseUrl">API基础URL(如果为null则从设置获取)</param>
|
|
|
|
|
|
/// <param name="userToken">用户Token(如果为null则从设置获取)</param>
|
|
|
|
|
|
private static async Task<bool> UploadFileInternalAsync(string filePath, WhiteboardInfo whiteboard = null, string apiBaseUrl = null, string userToken = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 再次检查文件是否存在(可能在队列等待时被删除)
|
|
|
|
|
|
if (!File.Exists(filePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查文件扩展名
|
|
|
|
|
|
var fileExtension = Path.GetExtension(filePath).ToLower();
|
2026-01-01 18:15:33 +08:00
|
|
|
|
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".xml" && fileExtension != ".zip")
|
2025-11-02 10:30:36 +08:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-08 21:01:50 +08:00
|
|
|
|
// 检查文件大小(最大10MB,ZIP文件可能更大,允许50MB)
|
2025-11-02 10:30:36 +08:00
|
|
|
|
var fileInfo = new FileInfo(filePath);
|
2025-11-08 21:01:50 +08:00
|
|
|
|
long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024;
|
|
|
|
|
|
if (fileInfo.Length > maxSize)
|
2025-11-02 10:30:36 +08:00
|
|
|
|
{
|
2025-11-08 21:01:50 +08:00
|
|
|
|
LogHelper.WriteLogToFile($"上传失败:文件过大({fileInfo.Length / 1024 / 1024}MB),超过{maxSize / 1024 / 1024}MB限制", LogHelper.LogType.Error);
|
2025-11-02 10:30:36 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
// 如果白板信息未提供,则重新获取
|
|
|
|
|
|
if (whiteboard == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var selectedClassName = MainWindow.Settings?.Dlass?.SelectedClassName;
|
|
|
|
|
|
if (string.IsNullOrEmpty(selectedClassName))
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile("上传失败:未选择班级", LogHelper.LogType.Error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
userToken = userToken ?? MainWindow.Settings?.Dlass?.UserToken;
|
|
|
|
|
|
if (string.IsNullOrEmpty(userToken))
|
2025-11-02 10:11:15 +08:00
|
|
|
|
{
|
2025-11-02 10:30:36 +08:00
|
|
|
|
LogHelper.WriteLogToFile("上传失败:未设置用户Token", LogHelper.LogType.Error);
|
2025-11-02 10:11:15 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
apiBaseUrl = apiBaseUrl ?? MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
|
|
|
|
|
|
|
|
|
|
|
|
// 创建API客户端并获取白板信息
|
|
|
|
|
|
using (var apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken))
|
|
|
|
|
|
{
|
|
|
|
|
|
var authData = new
|
|
|
|
|
|
{
|
|
|
|
|
|
app_id = APP_ID,
|
|
|
|
|
|
app_secret = APP_SECRET,
|
|
|
|
|
|
user_token = userToken
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var authResult = await apiClient.PostAsync<AuthWithTokenResponse>("/api/whiteboard/framework/auth-with-token", authData, requireAuth: false);
|
|
|
|
|
|
|
|
|
|
|
|
if (authResult == null || !authResult.Success || authResult.Whiteboards == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile("上传失败:无法获取白板信息", LogHelper.LogType.Error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找匹配班级的白板
|
|
|
|
|
|
whiteboard = authResult.Whiteboards
|
|
|
|
|
|
.FirstOrDefault(w => !string.IsNullOrEmpty(w.ClassName) && w.ClassName == selectedClassName);
|
|
|
|
|
|
|
|
|
|
|
|
if (whiteboard == null || string.IsNullOrEmpty(whiteboard.BoardId) || string.IsNullOrEmpty(whiteboard.SecretKey))
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"上传失败:未找到班级'{selectedClassName}'对应的白板", LogHelper.LogType.Error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取API基础URL和用户Token(如果未提供)
|
|
|
|
|
|
apiBaseUrl = apiBaseUrl ?? MainWindow.Settings?.Dlass?.ApiBaseUrl ?? "https://dlass.tech";
|
|
|
|
|
|
userToken = userToken ?? MainWindow.Settings?.Dlass?.UserToken;
|
|
|
|
|
|
|
|
|
|
|
|
// 准备上传参数
|
|
|
|
|
|
var fileName = Path.GetFileNameWithoutExtension(filePath);
|
|
|
|
|
|
var title = fileName;
|
2025-11-08 21:01:50 +08:00
|
|
|
|
string fileType;
|
|
|
|
|
|
string tags;
|
|
|
|
|
|
if (fileExtension == ".zip")
|
|
|
|
|
|
{
|
|
|
|
|
|
fileType = "多页面墨迹压缩包";
|
|
|
|
|
|
tags = "自动上传,多页面,zip,压缩包";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (fileExtension == ".icstk")
|
|
|
|
|
|
{
|
|
|
|
|
|
fileType = "墨迹文件";
|
|
|
|
|
|
tags = "自动上传,墨迹,icstk";
|
|
|
|
|
|
}
|
2026-01-01 18:15:33 +08:00
|
|
|
|
else if (fileExtension == ".xml")
|
|
|
|
|
|
{
|
|
|
|
|
|
fileType = "XML文件";
|
|
|
|
|
|
tags = "自动上传,xml";
|
|
|
|
|
|
}
|
2025-11-08 21:01:50 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
fileType = "笔记";
|
|
|
|
|
|
tags = "自动上传,笔记,png";
|
|
|
|
|
|
}
|
2025-11-02 10:30:36 +08:00
|
|
|
|
var description = $"自动上传的{fileType} - {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
|
2025-11-02 10:11:15 +08:00
|
|
|
|
|
2025-11-02 10:30:36 +08:00
|
|
|
|
// 创建API客户端并上传文件
|
|
|
|
|
|
using (var apiClient = new DlassApiClient(APP_ID, APP_SECRET, apiBaseUrl, userToken))
|
|
|
|
|
|
{
|
2025-11-02 10:11:15 +08:00
|
|
|
|
var uploadResult = await apiClient.UploadNoteAsync<UploadNoteResponse>(
|
|
|
|
|
|
"/api/whiteboard/upload_note",
|
2025-11-02 10:30:36 +08:00
|
|
|
|
filePath,
|
2025-11-02 10:11:15 +08:00
|
|
|
|
whiteboard.BoardId,
|
|
|
|
|
|
whiteboard.SecretKey,
|
|
|
|
|
|
title,
|
|
|
|
|
|
description,
|
|
|
|
|
|
tags);
|
|
|
|
|
|
|
|
|
|
|
|
if (uploadResult != null && uploadResult.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"笔记上传成功:{fileName} -> {uploadResult.FileUrl}", LogHelper.LogType.Event);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.WriteLogToFile($"上传失败:服务器响应失败 - {uploadResult?.Message ?? "未知错误"}", LogHelper.LogType.Error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2025-11-02 10:46:16 +08:00
|
|
|
|
// 记录错误信息,抛出异常以便调用方判断是否可重试
|
2025-11-02 10:11:15 +08:00
|
|
|
|
LogHelper.WriteLogToFile($"上传笔记时出错: {ex.Message}", LogHelper.LogType.Error);
|
2025-11-02 10:46:16 +08:00
|
|
|
|
throw;
|
2025-11-02 10:11:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-02 10:46:16 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 判断错误是否可重试(超时、网络错误等)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static bool IsRetryableError(string filePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查文件是否存在
|
|
|
|
|
|
if (!File.Exists(filePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false; // 文件不存在,不可重试
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查文件扩展名
|
|
|
|
|
|
var fileExtension = Path.GetExtension(filePath).ToLower();
|
2026-01-01 18:15:33 +08:00
|
|
|
|
if (fileExtension != ".png" && fileExtension != ".icstk" && fileExtension != ".xml" && fileExtension != ".zip")
|
2025-11-02 10:46:16 +08:00
|
|
|
|
{
|
|
|
|
|
|
return false; // 文件格式错误,不可重试
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查文件大小
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var fileInfo = new FileInfo(filePath);
|
2025-11-08 21:01:50 +08:00
|
|
|
|
long maxSize = fileExtension == ".zip" ? 50 * 1024 * 1024 : 10 * 1024 * 1024;
|
|
|
|
|
|
if (fileInfo.Length > maxSize)
|
2025-11-02 10:46:16 +08:00
|
|
|
|
{
|
|
|
|
|
|
return false; // 文件过大,不可重试
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return false; // 无法读取文件信息,不可重试
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 其他错误(超时、网络错误等)可以重试
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-11-02 10:11:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-29 17:26:47 +08:00
|
|
|
|
|