From fa48e73b84a703a47e5da448298167e48f00df94 Mon Sep 17 00:00:00 2001 From: fuenmao <980740792@qq.com> Date: Fri, 18 Sep 2026 16:08:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B8=E5=85=B3bug=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Employee/OnboardingEmployeeService.cs | 2 + .../OnboardingEntryConfigService.cs | 18 +++++ .../OnboardingPublicSubmissionService.cs | 9 ++- .../OnboardingPublicSubmissionController.cs | 65 ++++++++++++++++++- 4 files changed, 90 insertions(+), 4 deletions(-) diff --git a/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs b/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs index fc50873..0c93645 100644 --- a/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs +++ b/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs @@ -345,6 +345,8 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService .Where(item => item > 0) .Distinct() .ToList(); + if (fileIds.Count > 10) + throw Oops.Bah("转正材料文件总数不能超过 10 份"); var files = fileIds.Count == 0 ? new List() : await _db.Queryable() diff --git a/SimpleAdmin.Application/Services/Onboarding/EntryConfig/OnboardingEntryConfigService.cs b/SimpleAdmin.Application/Services/Onboarding/EntryConfig/OnboardingEntryConfigService.cs index 3cf271d..ed4e7c4 100644 --- a/SimpleAdmin.Application/Services/Onboarding/EntryConfig/OnboardingEntryConfigService.cs +++ b/SimpleAdmin.Application/Services/Onboarding/EntryConfig/OnboardingEntryConfigService.cs @@ -142,6 +142,24 @@ public class OnboardingEntryConfigService : IOnboardingEntryConfigService throw Oops.Bah("资料名称不能为空"); if (input.MaxFileCount <= 0) throw Oops.Bah("最大文件数量必须大于0"); + if (input.MaxFileSizeMb.HasValue && (input.MaxFileSizeMb.Value <= 0 || input.MaxFileSizeMb.Value > 20)) + throw Oops.Bah("单个文件大小必须设置在1至20MB之间"); + + var supportedExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "png", "jpg", "jpeg", "gif", "bmp", "webp", "pdf" + }; + var allowedExtensions = (input.AllowedExtensions ?? string.Empty) + .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(item => item.TrimStart('.').ToLowerInvariant()) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + if (allowedExtensions.Count == 0) + allowedExtensions.AddRange(new[] { "png", "jpg", "jpeg", "pdf" }); + var unsupportedExtensions = allowedExtensions.Where(item => !supportedExtensions.Contains(item)).ToList(); + if (unsupportedExtensions.Count > 0) + throw Oops.Bah($"不支持的材料扩展名:{string.Join("、", unsupportedExtensions)},仅允许图片或PDF文件"); + input.AllowedExtensions = string.Join(",", allowedExtensions); var materialCodeExists = await _db.Queryable() .AnyAsync(x => x.MaterialCode == input.MaterialCode && x.Id != input.Id); diff --git a/SimpleAdmin.Application/Services/Onboarding/PublicSubmission/OnboardingPublicSubmissionService.cs b/SimpleAdmin.Application/Services/Onboarding/PublicSubmission/OnboardingPublicSubmissionService.cs index 0fb0db1..3c46443 100644 --- a/SimpleAdmin.Application/Services/Onboarding/PublicSubmission/OnboardingPublicSubmissionService.cs +++ b/SimpleAdmin.Application/Services/Onboarding/PublicSubmission/OnboardingPublicSubmissionService.cs @@ -5,6 +5,7 @@ namespace SimpleAdmin.Application; /// public class OnboardingPublicSubmissionService : IOnboardingPublicSubmissionService { + private const int PublicMaterialMaxFileSizeMb = 20; private readonly SqlSugarScope _db; private readonly IOnboardingContractSnapshotService _snapshotService; private readonly IOnboardingIdentityValidationService _identityValidationService; @@ -233,10 +234,12 @@ public class OnboardingPublicSubmissionService : IOnboardingPublicSubmissionServ if (suffixNotAllowed) throw Oops.Bah("该材料不允许上传此文件类型"); - var fileTooLarge = requirement.MaxFileSizeMb.HasValue - && input.FileLength > requirement.MaxFileSizeMb.Value * 1024L * 1024L; + var maxFileSizeMb = requirement.MaxFileSizeMb.HasValue + ? Math.Min(requirement.MaxFileSizeMb.Value, PublicMaterialMaxFileSizeMb) + : PublicMaterialMaxFileSizeMb; + var fileTooLarge = input.FileLength > maxFileSizeMb * 1024L * 1024L; if (fileTooLarge) - throw Oops.Bah("文件超过允许的大小"); + throw Oops.Bah($"单个文件不能超过{maxFileSizeMb}MB,请压缩后重新上传"); var materials = await _db.Queryable() .Where(item => item.TaskId == task.Id && item.MaterialTypeId == input.MaterialTypeId) diff --git a/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicSubmissionController.cs b/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicSubmissionController.cs index c63435e..35d7a8a 100644 --- a/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicSubmissionController.cs +++ b/SimpleAdmin.Web.Core/Controllers/Application/Onboarding/OnboardingPublicSubmissionController.cs @@ -50,7 +50,7 @@ public class OnboardingPublicSubmissionController /// [HttpPost("upload-material")] [Consumes("multipart/form-data")] - [RequestSizeLimit(20 * 1024 * 1024)] + [RequestSizeLimit(21 * 1024 * 1024)] public async Task UploadMaterial( [FromQuery] string token, [FromQuery] long materialTypeId, @@ -70,6 +70,7 @@ public class OnboardingPublicSubmissionController FileLength = file.Length, TaskMaterialId = taskMaterialId }); + await ValidateMaterialFileContent(file); var fileId = await _fileService.UploadFile(SysDictConst.FILE_ENGINE_ALIYUN_OSS, file); await _service.SaveMaterial(new OnboardingSaveMaterialInput @@ -80,4 +81,66 @@ public class OnboardingPublicSubmissionController TaskMaterialId = taskMaterialId }); } + + private static async Task ValidateMaterialFileContent(IFormFile file) + { + var suffix = global::System.IO.Path.GetExtension(file.FileName).TrimStart('.').ToLowerInvariant(); + var supported = suffix is "png" or "jpg" or "jpeg" or "gif" or "bmp" or "webp" or "pdf"; + if (!supported) + throw Oops.Bah("仅允许上传图片或PDF文件"); + + var header = new byte[12]; + var bytesRead = 0; + await using (var stream = file.OpenReadStream()) + { + while (bytesRead < header.Length) + { + var read = await stream.ReadAsync(header.AsMemory(bytesRead, header.Length - bytesRead)); + if (read == 0) break; + bytesRead += read; + } + } + + var contentMatchesSuffix = suffix switch + { + "jpg" or "jpeg" => bytesRead >= 3 && header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF, + "png" => bytesRead >= 8 + && header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47 + && header[4] == 0x0D && header[5] == 0x0A && header[6] == 0x1A && header[7] == 0x0A, + "gif" => bytesRead >= 6 + && header[0] == 0x47 && header[1] == 0x49 && header[2] == 0x46 + && header[3] == 0x38 && (header[4] == 0x37 || header[4] == 0x39) && header[5] == 0x61, + "bmp" => bytesRead >= 2 && header[0] == 0x42 && header[1] == 0x4D, + "webp" => bytesRead >= 12 + && header[0] == 0x52 && header[1] == 0x49 && header[2] == 0x46 && header[3] == 0x46 + && header[8] == 0x57 && header[9] == 0x45 && header[10] == 0x42 && header[11] == 0x50, + "pdf" => bytesRead >= 5 + && header[0] == 0x25 && header[1] == 0x50 && header[2] == 0x44 + && header[3] == 0x46 && header[4] == 0x2D, + _ => false + }; + if (!contentMatchesSuffix) + throw Oops.Bah("文件真实格式与扩展名不一致,请上传有效的图片或PDF文件"); + + if (suffix == "pdf") return; + + await using var imageStream = file.OpenReadStream(); + SkiaSharp.SKCodec codec; + try + { + codec = SkiaSharp.SKCodec.Create(imageStream); + } + catch + { + throw Oops.Bah("图片文件已损坏或格式不受支持"); + } + + using (codec) + { + if (codec == null || codec.Info.Width <= 0 || codec.Info.Height <= 0) + throw Oops.Bah("图片文件已损坏或格式不受支持"); + if ((long)codec.Info.Width * codec.Info.Height > 40_000_000L) + throw Oops.Bah("图片分辨率过大,请压缩后重新上传"); + } + } }