From c063f39f4dfd0c0042db4ecfad18f37f094afebc Mon Sep 17 00:00:00 2001 From: zpc Date: Tue, 23 Sep 2025 16:55:40 +0800 Subject: [PATCH] 321 --- .../Controllers/CommonController.cs | 21 ++- .../file/2025/0923/9e02f4efa31ccb89.xlsx | Bin 0 -> 10867 bytes ZR.ServiceCore/Services/SysFileService.cs | 126 ++++++++++++++---- 3 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 ZR.Admin.WebApi/wwwroot/file/2025/0923/9e02f4efa31ccb89.xlsx diff --git a/ZR.Admin.WebApi/Controllers/CommonController.cs b/ZR.Admin.WebApi/Controllers/CommonController.cs index 8b693d2..ca1797a 100644 --- a/ZR.Admin.WebApi/Controllers/CommonController.cs +++ b/ZR.Admin.WebApi/Controllers/CommonController.cs @@ -126,12 +126,31 @@ namespace ZR.Admin.WebApi.Controllers switch (storeType) { case StoreType.LOCAL: + logger.Info($"开始本地文件上传 - 文件名: {formFile.FileName}, 大小: {fileSize}KB, 用户: {HttpContext.GetName()}"); + string savePath = Path.Combine(WebHostEnvironment.WebRootPath); + logger.Info($"WebRootPath: {WebHostEnvironment.WebRootPath}"); + if (uploadDto.FileDir.IsEmpty()) { uploadDto.FileDir = OptionsSetting.Upload.LocalSavePath; + logger.Info($"使用默认保存路径: {uploadDto.FileDir}"); + } + else + { + logger.Info($"使用指定保存路径: {uploadDto.FileDir}"); + } + + try + { + sysfile = await SysFileService.SaveFileToLocal(savePath, uploadDto, HttpContext.GetName(), formFile); + logger.Info($"本地文件保存成功 - 文件ID: {sysfile.Id}, 访问URL: {sysfile.AccessUrl}"); + } + catch (Exception ex) + { + logger.Error(ex, $"本地文件保存失败 - 文件名: {formFile.FileName}, 错误: {ex.Message}"); + throw; } - sysfile = await SysFileService.SaveFileToLocal(savePath, uploadDto, HttpContext.GetName(), formFile); break; case StoreType.REMOTE: break; diff --git a/ZR.Admin.WebApi/wwwroot/file/2025/0923/9e02f4efa31ccb89.xlsx b/ZR.Admin.WebApi/wwwroot/file/2025/0923/9e02f4efa31ccb89.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..02aad158601697b77c33812f45d3abd3ff6c53a1 GIT binary patch literal 10867 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 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"); - if (!Directory.Exists(Path.GetDirectoryName(finalFilePath))) + string directoryPath = Path.GetDirectoryName(finalFilePath); + logger.Info($"检查目录是否存在: {directoryPath}"); + + if (!Directory.Exists(directoryPath)) { - Directory.CreateDirectory(Path.GetDirectoryName(finalFilePath)); + 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); - if (dto.Quality > 0 && isImageByExtension) + logger.Info($"文件类型检查 - 是否为图片: {isImageByExtension}, 压缩质量: {dto.Quality}"); + + try { - await SaveCompressedImageAsync(formFile, finalFilePath); - } - else - { - using (var stream = new FileStream(finalFilePath, FileMode.Create)) + if (dto.Quality > 0 && isImageByExtension) { - await formFile.CopyToAsync(stream); + 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, @@ -87,7 +140,20 @@ namespace ZR.ServiceCore.Services ClassifyType = clasifyType, CategoryId = dto.CategoryId, }; - file.Id = await InsertFile(file); + + 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) @@ -185,23 +251,37 @@ namespace ZR.ServiceCore.Services /// public static async Task SaveCompressedImageAsync(IFormFile formFile, string finalFilePath, int quality = 75) { - using (var image = await Image.LoadAsync(formFile.OpenReadStream())) + var logger = NLog.LogManager.GetCurrentClassLogger(); + logger.Info($"开始压缩图片 - 目标路径: {finalFilePath}, 压缩质量: {quality}"); + + try { - // 进行压缩和调整大小(可选) - //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)) + using (var image = await Image.LoadAsync(formFile.OpenReadStream())) { - await image.SaveAsync(stream, encoder); + 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) {