using YD_Event.Application.Common.Dtos; using YD_Event.Core.Options; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Options; using Microsoft.AspNetCore.Mvc; using YD_Event.Core.Infrastructure.Aliyun.Oss; namespace YD_Event.Application.Common; [ApiDescriptionSettings("通用服务")] public class FileService : IDynamicApiController, ITransient { private readonly IOptionsMonitor _ossOptionsMonitor; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IWebHostEnvironment _webHostEnvironment; private readonly IIdGenerator _idGenerator; private readonly OssService _ossService; public FileService( IOptionsMonitor ossOptionsMonitor, IHttpContextAccessor httpContextAccessor, IWebHostEnvironment webHostEnvironment, IIdGenerator idGenerator, OssService ossService) { _ossOptionsMonitor = ossOptionsMonitor; _httpContextAccessor = httpContextAccessor; _webHostEnvironment = webHostEnvironment; _idGenerator = idGenerator; _ossService = ossService; } /// /// 上传附件 /// /// 表单文件 /// 返回上传文件信息 [NonUnify] public async Task> Upload([Required] IFormFile file) { if (file is null or { Length: 0 }) { throw Oops.Oh("请上传文件"); } var options = _ossOptionsMonitor.CurrentValue; var now = DateTime.Today; string name = _idGenerator.Encode(_idGenerator.NewLong()); string extension = Path.GetExtension(file.FileName); if (string.IsNullOrWhiteSpace(extension)) { throw Oops.Bah("无效文件"); } string filePath = $"{now.Year}/{now.Month:D2}/{now.Day:D2}/"; string fileName = $"{filePath}{name}{extension}"; using var stream = file.OpenReadStream(); string url = await UploadStreamInternal(fileName, stream, options); return new List() { new() { Name = $"{name}{extension}", Url = url } }; } /// /// 上传文件流 (供其他服务调用) /// /// 文件名及路径 /// 文件流 /// 返回完整 URL [NonAction] public async Task UploadFileStreamAsync(string fileName, Stream stream) { var options = _ossOptionsMonitor.CurrentValue; return await UploadStreamInternal(fileName, stream, options); } /// /// 内部上传文件流 /// private async Task UploadStreamInternal(string fileName, Stream stream, OssConnectionOptions options) { if (!options.Enable) { // 本地存储 string basePath = Path.Combine(_webHostEnvironment.WebRootPath, options.Bucket ?? "uploads"); string fullPath = Path.Combine(basePath, fileName); string dir = Path.GetDirectoryName(fullPath); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } using var fileStream = System.IO.File.Create(fullPath); await stream.CopyToAsync(fileStream); var request = _httpContextAccessor.HttpContext?.Request; if (request != null) { return $"{request.Scheme}://{request.Host.Value}/{(options.Bucket ?? "uploads")}/{fileName}"; } return $"/{(options.Bucket ?? "uploads")}/{fileName}"; } else { // OSS 上传 return await _ossService.PutObjectAsync(fileName, stream); } } /// /// 下载文件 /// /// 文件 URL /// 文件流结果 [HttpGet("download")] public IActionResult Download([FromQuery] string url) { if (string.IsNullOrWhiteSpace(url)) throw Oops.Bah("参数错误"); var options = _ossOptionsMonitor.CurrentValue; try { if (!options.Enable) { // 本地下载 var uri = new Uri(url); string localPath = Path.Combine(_webHostEnvironment.WebRootPath, uri.AbsolutePath.TrimStart('/')); if (!System.IO.File.Exists(localPath)) throw Oops.Bah("文件不存在"); var stream = System.IO.File.OpenRead(localPath); return new FileStreamResult(stream, "application/octet-stream") { FileDownloadName = Path.GetFileName(localPath) }; } else { // OSS 下载 // 解析 objectName (移除 Domain 部分) string domainUrl = options.Domain.TrimEnd('/') + "/"; string objectName = url; if (url.StartsWith(domainUrl, StringComparison.OrdinalIgnoreCase)) { objectName = url.Substring(domainUrl.Length); } else { var uri = new Uri(url); objectName = uri.AbsolutePath.TrimStart('/'); } var contentStream = _ossService.GetObject(objectName); return new FileStreamResult(contentStream, "application/octet-stream") { FileDownloadName = Path.GetFileName(objectName) }; } } catch (Exception ex) { throw Oops.Bah($"下载失败: {ex.Message}"); } } /// /// 删除文件 /// /// 文件 URL [HttpDelete("delete")] public IActionResult Delete([FromQuery] string url) { if (string.IsNullOrWhiteSpace(url)) throw Oops.Bah("参数错误"); var options = _ossOptionsMonitor.CurrentValue; try { if (!options.Enable) { // 本地删除 var uri = new Uri(url); string localPath = Path.Combine(_webHostEnvironment.WebRootPath, uri.AbsolutePath.TrimStart('/')); if (System.IO.File.Exists(localPath)) { System.IO.File.Delete(localPath); } } else { // OSS 删除 string domainUrl = options.Domain.TrimEnd('/') + "/"; string objectName = url; if (url.StartsWith(domainUrl, StringComparison.OrdinalIgnoreCase)) { objectName = url.Substring(domainUrl.Length); } else { var uri = new Uri(url); objectName = uri.AbsolutePath.TrimStart('/'); } _ossService.DeleteObject(objectName); } return new JsonResult(new { success = true, msg = "删除成功" }); } catch (Exception ex) { throw Oops.Bah($"删除失败: {ex.Message}"); } } }