This commit is contained in:
Zhe Fang
2025-12-28 21:10:02 -05:00
parent 884026594b
commit 0787f5b111
2 changed files with 22 additions and 46 deletions

View File

@@ -15,7 +15,7 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
private SMB2Client? _client;
private ISMBFileStore? _fileStore;
// 保存配置对象的引用,它是我们的“真理来源”
// 保存配置对象的引用
private readonly MediaFolder _config;
// 缓存解析出来的 Share 名称,因为 TreeConnect 要用
@@ -26,8 +26,6 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
_config = config ?? throw new ArgumentNullException(nameof(config));
// 在构造时就解析好 Share 名称,避免后续重复解析
// 假设 URI 是 smb://host/ShareName/Folder/Sub
// 我们需要提取 "ShareName"
var uri = _config.GetStandardUri();
// Segments[0] 是 "/", Segments[1] 是 "ShareName/"
@@ -48,16 +46,16 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
{
_client = new SMB2Client();
// 1. 连接主机
// 连接主机
bool connected = _client.Connect(_config.UriHost, SMBTransportType.DirectTCPTransport);
if (!connected) return false;
// 2. 登录
// 登录
var status = _client.Login(string.Empty, _config.UserName, _config.Password);
if (status != NTStatus.STATUS_SUCCESS) return false;
// 3. 连接共享目录 (TreeConnect)
// 注意:SMBLibrary 必须先连接到 Share后续所有文件操作都是基于这个 Share 的相对路径
// 连接共享目录 (TreeConnect)
// SMBLibrary 必须先连接到 Share后续所有文件操作都是基于这个 Share 的相对路径
if (string.IsNullOrEmpty(_shareName)) return false;
_fileStore = _client.TreeConnect(_shareName, out status);
@@ -161,7 +159,6 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
{
if (_fileStore == null || file == null) return null;
// ★ 核心简化:直接把对象扔进去,获取路径
string smbPath = GetPathRelativeToShare(file);
var ret = _fileStore.CreateFile(out object handle, out FileStatus status, smbPath,
@@ -184,9 +181,6 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
_client?.Disconnect();
}
// =========================================================
// ★ 私有魔法方法:处理所有令人头大的路径逻辑
// =========================================================
private string GetPathRelativeToShare(FileCacheEntity? entity)
{
Uri targetUri;
@@ -200,29 +194,17 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
targetUri = new Uri(entity.Uri);
}
// 1. 获取绝对路径
// ★★★ 关键修正:必须解码!把 %20 变回空格 ★★★
// targetUri.AbsolutePath -> "/Share/My%20Music/Song.mp3"
// Uri.UnescapeDataString -> "/Share/My Music/Song.mp3"
string absolutePath = Uri.UnescapeDataString(targetUri.AbsolutePath);
// 2. 移除 ShareName 部分
// 确保移除开头的 /
string cleanPath = absolutePath.TrimStart('/');
// 找到 ShareName 后的第一个斜杠
int slashIndex = cleanPath.IndexOf('/');
if (slashIndex == -1)
{
// 如果没有斜杠,说明就是 Share 根目录
return string.Empty;
}
// 截取 Share 之后的部分
string relativePath = cleanPath.Substring(slashIndex + 1);
// 3. 转换为 Windows 风格的反斜杠 (SMB 协议要求)
return relativePath.Replace("/", "\\");
}
}

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections.Generic;
using System.IO;
@@ -18,9 +19,9 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
{
_config = config ?? throw new ArgumentNullException(nameof(config));
// 1. 构建 BaseAddress (只包含 http://host:port/)
// 构建 BaseAddress (只包含 http://host:port/)
// MediaFolder.GetStandardUri() 返回的是带路径的完整 URI (http://host:port/path)
// 我们需要提取出根用于初始化 WebDavClient
// 提取出根用于初始化 WebDavClient
var fullUri = _config.GetStandardUri();
// 提取 "http://host:port"
@@ -52,7 +53,6 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
{
var list = new List<FileCacheEntity>();
// 1. 确定目标 URI
Uri targetUri;
if (parentFolder == null)
{
@@ -63,60 +63,54 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
targetUri = new Uri(parentFolder.Uri);
}
// 2. 发送请求 (使用绝对 URI)
// WebDavClient 允许传入绝对路径,它会自动处理
var result = await _client.Propfind(targetUri.AbsoluteUri);
if (result.IsSuccessful)
{
// 3. 准备父级 URI 字符串 (用于填充 Entity)
// 确保以 / 结尾,方便后续逻辑判断或数据库查询
string parentUriString = targetUri.AbsoluteUri;
if (!parentUriString.EndsWith("/")) parentUriString += "/";
// WebDAV 可能会把文件夹自己作为结果返回,我们需要过滤它
// 比较时忽略末尾斜杠
string targetPathClean = targetUri.AbsolutePath.TrimEnd('/');
foreach (var res in result.Resources)
{
// res.Uri 通常是相对路径,例如 "/dav/music/file.mp3"
// 我们需要将其转换为绝对 URI
var itemUri = new Uri(_baseAddress, res.Uri);
// 过滤掉文件夹自身
// 比较 AbsolutePath (例如 /dav/music vs /dav/music)
// 过滤掉文件夹自身
if (itemUri.AbsolutePath.TrimEnd('/') == targetPathClean) continue;
// 获取文件名 (解码)
// res.DisplayName 有时候是空的,这时候需要从 Uri 解析
string name = res.DisplayName;
string? name = res.DisplayName;
if (string.IsNullOrEmpty(name))
{
// 取最后一段,忽略末尾斜杠
name = itemUri.AbsolutePath.TrimEnd('/').Split('/').Last();
name = System.Net.WebUtility.UrlDecode(name);
}
if (string.IsNullOrEmpty(name)) continue;
if (name.StartsWith(".")) continue;
bool isDir = res.IsCollection;
if (!isDir)
{
string extension = System.IO.Path.GetExtension(name);
// 如果后缀为空或不在白名单,跳过
if (string.IsNullOrEmpty(extension) || !FileHelper.AllSupportedExtensions.Contains(extension)) continue;
}
list.Add(new FileCacheEntity
{
MediaFolderId = _config.Id,
// 记录父级 URI (保持传入时的形式,或者统一标准)
// 注意:对于 WebDAVParentUri 最好不带末尾斜杠,除非是根
ParentUri = parentFolder?.Uri ?? _config.GetStandardUri().AbsoluteUri,
// ★ 存储完整的 http://... 标准 URI
Uri = itemUri.AbsoluteUri,
FileName = name,
IsDirectory = res.IsCollection,
// WebDAV 通常能提供这些信息
FileSize = res.ContentLength ?? 0,
LastModified = res.LastModifiedDate
LastModified = res.LastModifiedDate ?? DateTime.MinValue,
});
}
}