using System.Net.Http.Headers; using System.Text; using Foxel.Services.Attributes; using Foxel.Services.Configuration; using Microsoft.Extensions.Logging; namespace Foxel.Services.Storage.Providers; [StorageProvider(StorageType.WebDAV)] public class WebDavStorageProvider(IConfigService configService, ILogger logger) : IStorageProvider { private HttpClient CreateClient() { var httpClient = new HttpClient(); var userName = configService["Storage:WebDAVUserName"]; var password = configService["Storage:WebDAVPassword"]; if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) { var authValue = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{userName}:{password}")); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authValue); } return httpClient; } public async Task SaveAsync(Stream fileStream, string fileName, string contentType) { try { string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/'); string basePath = configService["Storage:WebDAVBasePath"].Trim('/'); // 创建唯一的文件存储路径 string currentDate = DateTime.Now.ToString("yyyy/MM"); string ext = Path.GetExtension(fileName); string newFileName = $"{Guid.NewGuid()}{ext}"; string relativePath = $"{basePath}/{currentDate}/{newFileName}"; // 确保目录存在 await EnsureDirectoryExistsAsync($"{basePath}/{currentDate}"); // 上传文件内容 var requestUri = $"{webDavServerUrl}/{relativePath}"; using var client = CreateClient(); using var content = new StreamContent(fileStream); content.Headers.ContentType = new MediaTypeHeaderValue(contentType); using var request = new HttpRequestMessage(HttpMethod.Put, requestUri); request.Content = content; var response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); return relativePath; } catch (Exception ex) { logger.LogError(ex, "上传文件到WebDAV时出错"); throw; } } public async Task DeleteAsync(string storagePath) { try { if (string.IsNullOrEmpty(storagePath)) return; string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/'); var requestUri = $"{webDavServerUrl}/{storagePath}"; using var client = CreateClient(); var response = await client.DeleteAsync(requestUri); if (response.StatusCode != System.Net.HttpStatusCode.NotFound) { response.EnsureSuccessStatusCode(); } } catch (Exception ex) { logger.LogWarning(ex, "从WebDAV删除文件时出错"); } } public string GetUrl(string storagePath) { try { if (string.IsNullOrEmpty(storagePath)) return "/images/unavailable.gif"; string publicUrl = configService["Storage:WebDAVPublicUrl"].TrimEnd('/'); string serverUrl = configService["AppSettings:ServerUrl"]; if (!string.IsNullOrEmpty(publicUrl)) { return $"{publicUrl}/{storagePath}"; } return $"{serverUrl}/api/picture/proxy?path={Uri.EscapeDataString(storagePath)}"; } catch (Exception ex) { logger.LogError(ex, "生成WebDAV文件URL时出错"); return "/images/unavailable.gif"; } } public async Task DownloadFileAsync(string storagePath) { try { if (string.IsNullOrEmpty(storagePath)) { throw new ArgumentException("存储路径不能为空"); } string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/'); // 创建临时目录 var tempDir = Path.Combine(Path.GetTempPath(), "FoxelWebDAVTemp"); if (!Directory.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } // 创建临时文件名 string fileName = Path.GetFileName(storagePath); string tempFilePath = Path.Combine(tempDir, fileName); // 下载文件 var requestUri = $"{webDavServerUrl}/{storagePath}"; using var client = CreateClient(); using var request = new HttpRequestMessage(HttpMethod.Get, requestUri); var response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); await using var fileStream = new FileStream(tempFilePath, FileMode.Create); await response.Content.CopyToAsync(fileStream); return tempFilePath; } catch (Exception ex) { logger.LogError(ex, "从WebDAV下载文件时出错"); throw; } } /// /// 确保WebDAV上的目录存在 /// private async Task EnsureDirectoryExistsAsync(string directoryPath) { try { string webDavServerUrl = configService["Storage:WebDAVServerUrl"].TrimEnd('/'); var requestUri = $"{webDavServerUrl}/{directoryPath}"; using var client = CreateClient(); // 检查目录是否存在 - 使用新的请求对象 using var headRequest = new HttpRequestMessage(HttpMethod.Head, requestUri); var response = await client.SendAsync(headRequest); if (response.IsSuccessStatusCode) return; // 创建目录 - 使用新的请求对象 using var mkcolRequest = new HttpRequestMessage(new HttpMethod("MKCOL"), requestUri); response = await client.SendAsync(mkcolRequest); // 处理状态码 if (response.StatusCode == System.Net.HttpStatusCode.Conflict || response.StatusCode == System.Net.HttpStatusCode.NotFound) { // 递归创建父目录 var parentPath = Path.GetDirectoryName(directoryPath.TrimEnd('/'))?.Replace('\\', '/'); if (!string.IsNullOrEmpty(parentPath)) { await EnsureDirectoryExistsAsync(parentPath); using var retryRequest = new HttpRequestMessage(new HttpMethod("MKCOL"), requestUri); response = await client.SendAsync(retryRequest); if (response.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed) { using var putRequest = new HttpRequestMessage(HttpMethod.Put, $"{requestUri}/.dummy"); putRequest.Content = new StringContent(string.Empty); await client.SendAsync(putRequest); } else { response.EnsureSuccessStatusCode(); } } } else if (response.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed) { using var putRequest = new HttpRequestMessage(HttpMethod.Put, $"{requestUri}/.dummy"); putRequest.Content = new StringContent(string.Empty); var putResponse = await client.SendAsync(putRequest); putResponse.EnsureSuccessStatusCode(); } else { response.EnsureSuccessStatusCode(); } } catch (Exception ex) { logger.LogError(ex, "确保WebDAV目录存在时出错"); throw; } } }