using Infrastructure; using Infrastructure.Attribute; using Infrastructure.Enums; using Infrastructure.Model; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; using System.Net; using System.Security.Cryptography; using System.Text; using ZR.Common; using ZR.Model.Dto; using ZR.Model.System; namespace ZR.ServiceCore.Services { /// /// 文件管理 /// [AppService(ServiceType = typeof(ISysFileService), ServiceLifetime = LifeTime.Transient)] public class SysFileService : BaseService, ISysFileService { private string domainUrl = AppSettings.GetConfig("ALIYUN_OSS:domainUrl"); private readonly ISysConfigService SysConfigService; private OptionsSetting OptionsSetting; public SysFileService(ISysConfigService sysConfigService, IOptions options) { SysConfigService = sysConfigService; OptionsSetting = options.Value; } /// /// 存储本地 /// /// 存储根目录 /// 上传的文件流 /// 分类类型 /// /// /// public async Task SaveFileToLocal(string rootPath, UploadDto dto, string userName, string clasifyType, IFormFile formFile) { var logger = NLog.LogManager.GetCurrentClassLogger(); logger.Info($"SaveFileToLocal开始 - 根路径: {rootPath}, 用户: {userName}"); var fileName = dto.FileName; var fileDir = dto.FileDir; string fileExt = Path.GetExtension(formFile.FileName); fileName = (fileName.IsEmpty() ? HashFileName() : fileName) + fileExt; logger.Info($"文件名处理完成 - 原始文件名: {dto.FileName}, 最终文件名: {fileName}, 扩展名: {fileExt}"); string filePath = GetdirPath(fileDir); string finalFilePath = Path.Combine(rootPath, filePath, fileName); double fileSize = Math.Round(formFile.Length / 1024.0, 2); logger.Info($"文件路径构建完成 - 相对路径: {filePath}, 完整路径: {finalFilePath}, 文件大小: {fileSize}KB"); string directoryPath = Path.GetDirectoryName(finalFilePath); logger.Info($"检查目录是否存在: {directoryPath}"); if (!Directory.Exists(directoryPath)) { logger.Info($"目录不存在,开始创建目录: {directoryPath}"); try { Directory.CreateDirectory(directoryPath); logger.Info($"目录创建成功: {directoryPath}"); } catch (Exception ex) { logger.Error(ex, $"目录创建失败: {directoryPath}, 错误: {ex.Message}"); throw new Exception($"无法创建目录 {directoryPath}: {ex.Message}", ex); } } else { logger.Info($"目录已存在: {directoryPath}"); } // 检查目录写入权限 try { logger.Info($"检查目录写入权限: {directoryPath}"); var testFile = Path.Combine(directoryPath, $"test_{Guid.NewGuid()}.tmp"); File.WriteAllText(testFile, "test"); File.Delete(testFile); logger.Info($"目录写入权限检查通过: {directoryPath}"); } catch (Exception ex) { logger.Error(ex, $"目录写入权限检查失败: {directoryPath}, 错误: {ex.Message}"); throw new Exception($"目录 {directoryPath} 没有写入权限: {ex.Message}", ex); } // 常见的图片扩展名 var imageExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" }; // 检查扩展名是否在图片扩展名列表中 bool isImageByExtension = imageExtensions.Contains(fileExt); logger.Info($"文件类型检查 - 是否为图片: {isImageByExtension}, 压缩质量: {dto.Quality}"); try { if (dto.Quality > 0 && isImageByExtension) { logger.Info($"开始压缩图片保存: {finalFilePath}"); await SaveCompressedImageAsync(formFile, finalFilePath); logger.Info($"图片压缩保存完成: {finalFilePath}"); } else { logger.Info($"开始普通文件保存: {finalFilePath}"); using (var stream = new FileStream(finalFilePath, FileMode.Create)) { await formFile.CopyToAsync(stream); } logger.Info($"普通文件保存完成: {finalFilePath}"); } } catch (Exception ex) { logger.Error(ex, $"文件保存失败: {finalFilePath}, 错误: {ex.Message}"); throw new Exception($"文件保存失败: {ex.Message}", ex); } string uploadUrl = OptionsSetting.Upload.UploadUrl; string accessPath = string.Concat(filePath.Replace("\\", "/"), "/", fileName); Uri baseUri = new(uploadUrl); Uri fullUrl = new(baseUri, accessPath); logger.Info($"URL构建完成 - 上传URL: {uploadUrl}, 访问路径: {accessPath}, 完整访问URL: {fullUrl.AbsoluteUri}"); SysFile file = new(formFile.FileName, fileName, fileExt, fileSize + "kb", filePath, userName) { StoreType = (int)StoreType.LOCAL, FileType = formFile.ContentType, FileUrl = finalFilePath.Replace("\\", "/"), AccessUrl = fullUrl.AbsoluteUri, ClassifyType = clasifyType, CategoryId = dto.CategoryId, }; logger.Info($"开始保存文件信息到数据库 - 文件URL: {file.FileUrl}, 访问URL: {file.AccessUrl}"); try { file.Id = await InsertFile(file); logger.Info($"文件信息保存成功 - 文件ID: {file.Id}"); } catch (Exception ex) { logger.Error(ex, $"文件信息保存到数据库失败: {ex.Message}"); throw new Exception($"文件信息保存失败: {ex.Message}", ex); } logger.Info($"SaveFileToLocal完成 - 文件ID: {file.Id}, 访问URL: {file.AccessUrl}"); return file; } //public async Task SaveFileToLocal(string rootPath, string fileName, string fileDir, string userName, IFormFile formFile) //{ // return await SaveFileToLocal(rootPath, fileName, fileDir, userName, string.Empty, formFile); //} public async Task SaveFileToLocal(string rootPath, UploadDto dto, string userName, IFormFile formFile) { return await SaveFileToLocal(rootPath, dto, userName, dto.ClassifyType, formFile); } /// /// 上传文件到阿里云 /// /// /// /// /// public async Task SaveFileToAliyun(SysFile file, UploadDto dto, IFormFile formFile) { file.FileName = (file.FileName.IsEmpty() ? HashFileName() : file.FileName) + file.FileExt; file.StorePath = GetdirPath(file.StorePath); string finalPath = Path.Combine(file.StorePath, file.FileName); HttpStatusCode statusCode; if (dto.Quality > 0) { // 压缩图片 using var stream = new MemoryStream(); await CompressImageAsync(formFile, stream, dto.Quality); stream.Position = 0; statusCode = AliyunOssHelper.PutObjectFromFile(stream, finalPath, ""); } else { statusCode = AliyunOssHelper.PutObjectFromFile(formFile.OpenReadStream(), finalPath, ""); } if (statusCode != HttpStatusCode.OK) return file; file.StorePath = file.StorePath; file.FileUrl = finalPath; file.AccessUrl = string.Concat(domainUrl, "/", file.StorePath.Replace("\\", "/"), "/", file.FileName); file.Id = await InsertFile(file); return file; } /// /// 获取文件存储目录 /// /// /// 是否按年月日存储 /// public string GetdirPath(string storePath = "", bool byTimeStore = true) { DateTime date = DateTime.Now; string timeDir = date.ToString("yyyy/MMdd"); if (!string.IsNullOrEmpty(storePath)) { timeDir = Path.Combine(storePath, timeDir); } Console.WriteLine("文件存储目录" + timeDir); return timeDir.Replace("\\", "/"); } public string HashFileName(string str = null) { if (string.IsNullOrEmpty(str)) { str = Guid.NewGuid().ToString().ToLower(); } return BitConverter.ToString(MD5.HashData(Encoding.Default.GetBytes(str)), 4, 8).Replace("-", "").ToLower(); } public Task InsertFile(SysFile file) { return Insertable(file).ExecuteReturnSnowflakeIdAsync();//单条插入返回雪花ID; } /// /// 修改文件存储表 /// /// /// public int UpdateFile(SysFile model) { return Update(model, t => new { t.ClassifyType, }, true); } /// /// 压缩图片 /// /// /// /// /// public static async Task SaveCompressedImageAsync(IFormFile formFile, string finalFilePath, int quality = 75) { var logger = NLog.LogManager.GetCurrentClassLogger(); logger.Info($"开始压缩图片 - 目标路径: {finalFilePath}, 压缩质量: {quality}"); try { using (var image = await Image.LoadAsync(formFile.OpenReadStream())) { logger.Info($"图片加载成功 - 尺寸: {image.Width}x{image.Height}"); // 进行压缩和调整大小(可选) //image.Mutate(x => x.Resize(new ResizeOptions //{ // Mode = ResizeMode.Max, // Size = new Size(1920, 1080) // 限制最大尺寸,避免超大图片 //})); // 保存为压缩的 JPEG var encoder = new JpegEncoder { Quality = quality }; // 质量参数控制压缩程度 await using (var stream = new FileStream(finalFilePath, FileMode.Create)) { await image.SaveAsync(stream, encoder); } logger.Info($"图片压缩保存完成: {finalFilePath}"); } } catch (Exception ex) { logger.Error(ex, $"图片压缩失败: {finalFilePath}, 错误: {ex.Message}"); throw; } } private async Task CompressImageAsync(IFormFile file, Stream outputStream, int quality) { // 加载图片 using var image = await Image.LoadAsync(file.OpenReadStream()); // 设置 JPEG 编码器并指定质量 var encoder = new JpegEncoder { Quality = quality }; // 保存压缩后的图片到输出流 await image.SaveAsync(outputStream, encoder); } public async Task SaveFileToLocal(string rootPath, UploadDto dto, string userName, string fileName, byte[] fileByte) { var fileDir = dto.FileDir; string fileExt = Path.GetExtension(fileName); fileName = (fileName.IsEmpty() ? HashFileName() : fileName) + fileExt; string filePath = GetdirPath(fileDir); string finalFilePath = Path.Combine(rootPath, filePath, fileName); double fileSize = Math.Round(fileByte.Length / 1024.0, 2); if (!Directory.Exists(Path.GetDirectoryName(finalFilePath))) { Directory.CreateDirectory(Path.GetDirectoryName(finalFilePath)); } // 常见的图片扩展名 //var imageExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" }; // 检查扩展名是否在图片扩展名列表中 //bool isImageByExtension = imageExtensions.Contains(fileExt); using (var stream = new FileStream(finalFilePath, FileMode.Create)) { await stream.WriteAsync(fileByte, 0, fileByte.Length); } string uploadUrl = OptionsSetting.Upload.UploadUrl; string accessPath = string.Concat(filePath.Replace("\\", "/"), "/", fileName); Uri baseUri = new(uploadUrl); Uri fullUrl = new(baseUri, accessPath); SysFile file = new(fileName, fileName, fileExt, fileSize + "kb", filePath, userName) { StoreType = (int)StoreType.LOCAL, FileType = fileExt, FileUrl = finalFilePath.Replace("\\", "/"), AccessUrl = fullUrl.AbsoluteUri, ClassifyType = "", CategoryId = dto.CategoryId, }; file.Id = await InsertFile(file); return file; } public async Task SaveFileToAliyun(SysFile sysFile, UploadDto dto, byte[] file) { throw new NotImplementedException(); } } }