引言:文件上传功能的重要性与挑战
在现代 Web 应用开发中,文件上传是一个极其常见的需求。无论是用户头像上传、文档管理、图片分享平台,还是企业级的报表系统,都离不开稳定可靠的文件上传功能。然而,文件上传看似简单,实则暗藏玄机。开发者不仅要处理文件接收、存储等基本逻辑,还必须面对文件大小限制、类型验证、安全漏洞防护等一系列挑战。
ASP.NET 作为微软推出的企业级 Web 开发框架,提供了强大的文件上传控件和相关 API,帮助开发者高效实现文件上传功能。本文将深入探讨 ASP.NET 文件上传的实战技巧,重点讲解如何安全、高效地实现文件上传功能,让你轻松应对各种复杂场景。
一、ASP.NET 文件上传控件基础
1.1 FileUpload 控件简介
在 ASP.NET Web Forms 中,
FileUpload
控件是最常用的文件上传组件。它封装了 HTML 的
元素,提供了丰富的服务器端 API。
1.2 基本上传实现
下面是一个最基础的文件上传实现示例:
protected void btnUpload_Click(object sender, EventArgs e)
{
// 检查是否选择了文件
if (FileUpload1.HasFile)
{
try
{
// 获取文件名
string fileName = Path.GetFileName(FileUpload1.FileName);
// 指定上传路径(确保该目录存在且有写入权限)
string uploadPath = Server.MapPath(“~/Uploads/”);
// 保存文件
FileUpload1.SaveAs(Path.Combine(uploadPath, fileName));
// 显示成功信息
lblMessage.Text = “文件上传成功!”;
lblMessage.ForeColor = System.Drawing.Color.Green;
}
catch (Exception ex)
{
lblMessage.Text = “上传失败:” + ex.Message;
lblMessage.ForeColor = System.Drawing.Color.Red;
}
}
else
{
lblMessage.Text = “请选择要上传的文件!”;
lblMessage.ForeColor = System.Drawing.Color.Red;
}
}
1.3 获取文件信息
FileUpload 控件提供了多个属性来获取文件信息:
protected void btnUpload_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
// 文件名(不含路径)
string fileName = FileUpload1.FileName;
// 文件大小(字节)
int fileSize = FileUpload1.PostedFile.ContentLength;
// 文件 MIME 类型
string contentType = FileUpload1.PostedFile.ContentType;
// 文件扩展名
string extension = Path.GetExtension(fileName).ToLower();
// 显示文件信息
lblInfo.Text = $”文件名:{fileName}
” +
$”大小:{fileSize} 字节
” +
$”类型:{contentType}
” +
$”扩展名:{extension}”;
}
}
二、文件大小限制与处理
2.1 配置最大上传大小
ASP.NET 默认的上传大小限制是 4MB(4096KB)。要上传更大的文件,需要在
web.config
中配置:
参数说明:
maxRequestLength
:ASP.NET 处理的最大请求大小(KB)
executionTimeout
:请求超时时间(秒)
maxAllowedContentLength
:IIS 处理的最大请求大小(字节)
2.2 前端验证文件大小
在服务器端验证之前,可以通过 JavaScript 进行前端验证,提升用户体验:
2.3 服务器端大小验证
即使有前端验证,服务器端验证也是必不可少的:
protected void btnUpload_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
const int MAX_SIZE = 10 * 1024 * 1024; // 10MB
if (FileUpload1.PostedFile.ContentLength > MAX_SIZE)
{
lblMessage.Text = “文件大小不能超过10MB!”;
lblMessage.ForeColor = System.Drawing.Color.Red;
return;
}
// 继续上传逻辑…
}
}
三、文件类型验证策略
3.1 基于扩展名的验证
这是最简单但最不安全的验证方式:
private bool IsValidExtension(string fileName)
{
string[] allowedExtensions = { “.jpg”, “.jpeg”, “.png”, “.gif”, “.bmp” };
string extension = Path.GetExtension(fileName).ToLower();
return allowedExtensions.Contains(extension);
}
protected void btnUpload_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
if (!IsValidExtension(FileUpload1.FileName))
{
lblMessage.Text = “只允许上传图片文件(jpg, png, gif, bmp)!”;
lblMessage.ForeColor = System.Drawing.Color.Red;
return;
}
// 继续上传逻辑…
}
}
3.2 基于 MIME 类型的验证
比扩展名验证更可靠:
private bool IsValidMimeType(string mimeType)
{
string[] allowedMimeTypes = {
“image/jpeg”,
“image/png”,
“image/gif”,
“image/bmp”
};
return allowedMimeTypes.Contains(mimeType);
}
protected void btnUpload_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
string mimeType = FileUpload1.PostedFile.ContentType;
if (!IsValidMimeType(mimeType))
{
lblMessage.Text = “只允许上传图片文件!”;
lblMessage.ForeColor = System.Drawing.Color.Red;
return;
}
// 继续上传逻辑…
}
}
3.3 基于文件签名(Magic Number)的验证
这是最安全的验证方式,通过检查文件头部字节来判断真实文件类型:
public class FileSignatureValidator
{
// 文件签名映射
private static readonly Dictionary
{
{ “.jpg”, new byte[] { 0xFF, 0xD8, 0xFF } },
{ “.jpeg”, new byte[] { 0xFF, 0xD8, 0xFF } },
{ “.png”, new byte[] { 0x89, 0x50, 0x4E, 0x47 } },
{ “.gif”, new byte[] { 0x47, 0x49, 0x46 } },
{ “.pdf”, new byte[] { 0x25, 0x50, 0x44, 0x46 } },
{ “.doc”, new byte[] { 0xD0, 0xCF, 0x11, 0xE0 } },
{ “.docx”, new byte[] { 0x50, 0x4B, 0x03, 0x04 } }
};
public static bool IsValidFileSignature(HttpPostedFile file, string expectedExtension)
{
if (!FileSignatures.ContainsKey(expectedExtension))
return false;
byte[] signature = FileSignatures[expectedExtension];
byte[] fileSignature = new byte[signature.Length];
using (BinaryReader reader = new BinaryReader(file.InputStream))
{
fileSignature = reader.ReadBytes(signature.Length);
}
return signature.SequenceEqual(fileSignature);
}
}
protected void btnUpload_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
string extension = Path.GetExtension(FileUpload1.FileName).ToLower();
if (!FileSignatureValidator.IsValidFileSignature(FileUpload1.PostedFile, extension))
{
lblMessage.Text = “文件类型不匹配,可能已损坏或被篡改!”;
lblMessage.ForeColor = System.Drawing.Color.Red;
return;
}
// 继续上传逻辑…
}
}
四、文件名安全处理
4.1 防止路径遍历攻击
攻击者可能通过文件名包含
../
等字符来尝试访问系统目录:
private string SafeFileName(string originalFileName)
{
// 移除路径字符
string safeName = Path.GetFileName(originalFileName);
// 替换特殊字符
safeName = safeName.Replace(“..”, “”)
.Replace(“\\”, “”)
.Replace(“/”, “”)
.Replace(“:”, “”)
.Replace(“*”, “”)
.Replace(“?”, “”)
.Replace(“\””, “”)
.Replace(“<", "")
.Replace(">“, “”)
.Replace(“|”, “”);
return safeName;
}
4.2 防止文件名冲突
使用 GUID 或时间戳确保文件名唯一:
private string GenerateUniqueFileName(string originalFileName)
{
string extension = Path.GetExtension(originalFileName);
string uniqueName = $”{Guid.NewGuid()}{extension}”;
return uniqueName;
}
// 或者使用时间戳
private string GenerateTimestampFileName(string originalFileName)
{
string extension = Path.GetExtension(originalFileName);
string timestamp = DateTime.Now.ToString(“yyyyMMddHHmmssfff”);
return $”{timestamp}{extension}”;
}
4.3 完整的文件名处理流程
protected void btnUpload_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
// 1. 安全处理文件名
string safeFileName = SafeFileName(FileUpload1.FileName);
// 2. 生成唯一文件名
string uniqueFileName = GenerateUniqueFileName(safeFileName);
// 3. 构建完整路径
string uploadPath = Server.MapPath(“~/Uploads/”);
// 4. 确保目录存在
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
// 5. 保存文件
string fullPath = Path.Combine(uploadPath, uniqueFileName);
FileUpload1.SaveAs(fullPath);
lblMessage.Text = $”文件上传成功!新文件名:{uniqueFileName}”;
}
}
五、文件存储策略
5.1 本地文件系统存储
适用于中小规模应用:
public class LocalStorageProvider
{
private readonly string _baseDirectory;
public LocalStorageProvider(string baseDirectory)
{
_baseDirectory = baseDirectory;
}
public async Task
{
string uniqueName = $”{Guid.NewGuid()}{Path.GetExtension(fileName)}”;
string fullPath = Path.Combine(_baseDirectory, uniqueName);
using (var fileStreamWrite = new FileStream(fullPath, FileMode.Create))
{
await fileStream.CopyToAsync(fileStreamWrite);
}
return uniqueName;
}
public async Task
{
string fullPath = Path.Combine(_baseDirectory, fileName);
if (!File.Exists(fullPath))
throw new FileNotFoundException(“文件不存在”);
return new FileStream(fullPath, FileMode.Open, FileAccess.Read);
}
public async Task DeleteFileAsync(string fileName)
{
string fullPath = Path.Combine(_baseDirectory, fileName);
if (File.Exists(fullPath))
{
await Task.Run(() => File.Delete(fullPath));
}
}
}
5.2 数据库存储(BLOB)
适用于需要事务支持或集中管理的场景:
public class DatabaseStorageProvider
{
private readonly string _connectionString;
public DatabaseStorageProvider(string connectionString)
{
_connectionString = connectionString;
}
public async Task
{
string uniqueName = $”{Guid.NewGuid()}{Path.GetExtension(originalFileName)}”;
using (var connection = new SqlConnection(_connectionString))
{
await connection.OpenAsync();
using (var command = new SqlCommand(
“INSERT INTO Files (FileName, OriginalName, Content, UploadedBy, UploadedDate) ” +
“VALUES (@FileName, @OriginalName, @Content, @UploadedBy, @UploadedDate)”,
connection))
{
command.Parameters.AddWithValue(“@FileName”, uniqueName);
command.Parameters.AddWithValue(“@OriginalName”, originalFileName);
// 将流转换为字节数组
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream);
command.Parameters.AddWithValue(“@Content”, memoryStream.ToArray());
}
command.Parameters.AddWithValue(“@UploadedBy”, userId);
command.Parameters.AddWithValue(“@UploadedDate”, DateTime.Now);
await command.ExecuteNonQueryAsync();
}
}
return uniqueName;
}
public async Task
{
using (var connection = new SqlConnection(_connectionString))
{
await connection.OpenAsync();
using (var command = new SqlCommand(
“SELECT Content FROM Files WHERE FileName = @FileName”,
connection))
{
command.Parameters.AddWithValue(“@FileName”, fileName);
var result = await command.ExecuteScalarAsync();
return result as byte[];
}
}
}
}
5.3 云存储(Azure Blob Storage)
适用于大规模、高可用的应用:
using Azure.Storage.Blobs;
public class AzureBlobStorageProvider
{
private readonly BlobServiceClient _blobServiceClient;
private readonly string _containerName;
public AzureBlobStorageProvider(string connectionString, string containerName)
{
_blobServiceClient = new BlobServiceClient(connectionString);
_containerName = containerName;
}
public async Task
{
// 获取容器引用(如果不存在则创建)
BlobContainerClient containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
await containerClient.CreateIfNotExistsAsync();
// 生成唯一文件名
string uniqueName = $”{Guid.NewGuid()}{Path.GetExtension(fileName)}”;
// 获取 Blob 引用
BlobClient blobClient = containerClient.GetBlobClient(uniqueName);
// 上传文件
await blobClient.UploadAsync(fileStream, overwrite: true);
return uniqueName;
}
public async Task
{
BlobContainerClient containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
BlobClient blobClient = containerClient.GetBlobClient(fileName);
if (!await blobClient.ExistsAsync())
throw new FileNotFoundException(“文件不存在”);
var response = await blobClient.DownloadAsync();
return response.Value.Content;
}
}
六、异步上传与进度显示
6.1 异步处理提升性能
[WebMethod]
public static async Task
{
try
{
// 将 Base64 转换为字节数组
byte[] fileBytes = Convert.FromBase64String(base64File.Split(‘,’)[1]);
// 生成唯一文件名
string uniqueName = $”{Guid.NewGuid()}{Path.GetExtension(fileName)}”;
string uploadPath = HttpContext.Current.Server.MapPath(“~/Uploads/”);
// 异步保存文件
using (var memoryStream = new MemoryStream(fileBytes))
{
string fullPath = Path.Combine(uploadPath, uniqueName);
using (var fileStream = new FileStream(fullPath, FileMode.Create))
{
await memoryStream.CopyToAsync(fileStream);
}
}
return $”{{\”success\”:true,\”fileName\”:\”{uniqueName}\”}}”;
}
catch (Exception ex)
{
return $”{{\”success\”:false,\”error\”:\”{ex.Message}\”}}”;
}
}
6.2 前端 AJAX 上传与进度显示
6.3 一般处理程序(ASHX)实现
// FileHandler.ashx
<%@ WebHandler Language="C#" Class="FileHandler" %>
using System;
using System.IO;
using System.Threading.Tasks;
using System.Web;
public class FileHandler : IHttpAsyncHandler
{
public bool IsReusable => false;
public void ProcessRequest(HttpContext context)
{
// 同步方法占位
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
return ProcessRequestAsync(context).GetAwaiter();
}
public void EndProcessRequest(IAsyncResult result)
{
// 处理完成
}
private async Task ProcessRequestAsync(HttpContext context)
{
try
{
var request = context.Request;
if (request.Files.Count == 0)
{
context.Response.Write(“{\”success\”:false,\”error\”:\”没有文件\”}”);
return;
}
var file = request.Files[0];
// 验证文件大小
if (file.ContentLength > 10 * 1024 * 1024)
{
context.Response.Write(“{\”success\”:false,\”error\”:\”文件超过10MB限制\”}”);
return;
}
// 验证文件类型
string extension = Path.GetExtension(file.FileName).ToLower();
string[] allowedExtensions = { “.jpg”, “.jpeg”, “.png”, “.gif” };
if (!allowedExtensions.Contains(extension))
{
context.Response.Write(“{\”success\”:false,\”error\”:\”只允许上传图片文件\”}”);
return;
}
// 生成唯一文件名
string uniqueName = $”{Guid.NewGuid()}{extension}”;
string uploadPath = context.Server.MapPath(“~/Uploads/”);
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
// 异步保存
string fullPath = Path.Combine(uploadPath, uniqueName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
await file.InputStream.CopyToAsync(stream);
}
context.Response.Write($”{{\”success\”:true,\”fileName\”:\”{uniqueName}\”}}”);
}
catch (Exception ex)
{
context.Response.Write($”{{\”success\”:false,\”error\”:\”{ex.Message}\”}}”);
}
}
}
七、安全验证最佳实践
7.1 完整的安全验证流程
public class SecureFileUploader
{
private readonly int _maxFileSize;
private readonly string[] _allowedExtensions;
private readonly string[] _allowedMimeTypes;
private readonly string _uploadDirectory;
public SecureFileUploader(int maxFileSize, string[] allowedExtensions, string[] allowedMimeTypes, string uploadDirectory)
{
_maxFileSize = maxFileSize;
_allowedExtensions = allowedExtensions;
_allowedMimeTypes = allowedMimeTypes;
_uploadDirectory = uploadDirectory;
}
public async Task
{
var result = new UploadResult();
// 1. 检查文件是否存在
if (file == null || file.ContentLength == 0)
{
result.ErrorMessage = “文件不能为空”;
return result;
}
// 2. 验证文件大小
if (file.ContentLength > _maxFileSize)
{
result.ErrorMessage = $”文件大小不能超过 {_maxFileSize / (1024 * 1024)}MB”;
return result;
}
// 3. 获取文件信息
string originalFileName = Path.GetFileName(file.FileName);
string extension = Path.GetExtension(originalFileName).ToLower();
string mimeType = file.ContentType;
// 4. 验证扩展名
if (!_allowedExtensions.Contains(extension))
{
result.ErrorMessage = $”不支持的文件类型,允许的类型:{string.Join(“, “, _allowedExtensions)}”;
return result;
}
// 5. 验证 MIME 类型
if (!_allowedMimeTypes.Contains(mimeType))
{
result.ErrorMessage = “文件 MIME 类型不匹配”;
return result;
}
// 6. 验证文件签名(最重要)
if (!await FileSignatureValidator.IsValidFileSignatureAsync(file, extension))
{
result.ErrorMessage = “文件类型验证失败,文件可能已损坏或被篡改”;
return result;
}
// 7. 安全处理文件名
string safeFileName = SanitizeFileName(originalFileName);
string uniqueFileName = $”{Guid.NewGuid()}{extension}”;
// 8. 创建上传目录
if (!Directory.Exists(_uploadDirectory))
{
Directory.CreateDirectory(_uploadDirectory);
}
// 9. 保存文件
string fullPath = Path.Combine(_uploadDirectory, uniqueFileName);
await SaveFileAsync(file.InputStream, fullPath);
// 10. 记录日志
await LogUploadAsync(userId, originalFileName, uniqueFileName, file.ContentLength);
result.Success = true;
result.FileName = uniqueFileName;
result.OriginalFileName = originalFileName;
return result;
}
private async Task SaveFileAsync(Stream stream, string path)
{
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
{
await stream.CopyToAsync(fileStream);
}
}
private string SanitizeFileName(string fileName)
{
string safeName = Path.GetFileName(fileName);
return safeName.Replace(“..”, “”)
.Replace(“\\”, “”)
.Replace(“/”, “”)
.Replace(“:”, “”)
.Replace(“*”, “”)
.Replace(“?”, “”)
.Replace(“\””, “”)
.Replace(“<", "")
.Replace(">“, “”)
.Replace(“|”, “”);
}
private async Task LogUploadAsync(string userId, string originalName, string uniqueName, long size)
{
// 实现日志记录逻辑
// 可以写入数据库、文件或日志系统
}
}
public class UploadResult
{
public bool Success { get; set; }
public string FileName { get; set; }
public string OriginalFileName { get; set; }
public string ErrorMessage { get; set; }
}
// 扩展的文件签名验证(支持异步)
public class FileSignatureValidator
{
private static readonly Dictionary
{
{ “.jpg”, new byte[] { 0xFF, 0xD8, 0xFF } },
{ “.jpeg”, new byte[] { 0xFF, 0xD8, 0xFF } },
{ “.png”, new byte[] { 0x89, 0x50, 0x4E, 0x47 } },
{ “.gif”, new byte[] { 0x47, 0x49, 0x46 } },
{ “.pdf”, new byte[] { 0x25, 0x50, 0x44, 0x46 } },
{ “.doc”, new byte[] { 0xD0, 0xCF, 0x11, 0xE0 } },
{ “.docx”, new byte[] { 0x50, 0x4B, 0x03, 0x04 } },
{ “.xlsx”, new byte[] { 0x50, 0x4B, 0x03, 0x04 } },
{ “.zip”, new byte[] { 0x50, 0x4B, 0x03, 0x04 } }
};
public static async Task
{
if (!FileSignatures.ContainsKey(expectedExtension))
return false;
byte[] signature = FileSignatures[expectedExtension];
byte[] fileSignature;
// 读取文件头部字节
using (var binaryReader = new BinaryReader(file.InputStream))
{
// 重置流位置
file.InputStream.Position = 0;
fileSignature = binaryReader.ReadBytes(signature.Length);
// 重置流位置,以便后续保存
file.InputStream.Position = 0;
}
return signature.SequenceEqual(fileSignature);
}
}
7.2 使用示例
protected async void btnUpload_Click(object sender, EventArgs e)
{
if (!FileUpload1.HasFile)
{
lblMessage.Text = “请选择文件!”;
return;
}
var uploader = new SecureFileUploader(
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedExtensions: new[] { “.jpg”, “.jpeg”, “.png”, “.gif”, “.pdf” },
allowedMimeTypes: new[] { “image/jpeg”, “image/png”, “image/gif”, “application/pdf” },
uploadDirectory: Server.MapPath(“~/SecureUploads/”)
);
var result = await uploader.UploadFileAsync(FileUpload1.PostedFile, User.Identity.Name);
if (result.Success)
{
lblMessage.Text = $”上传成功!文件名:{result.FileName}”;
lblMessage.ForeColor = System.Drawing.Color.Green;
}
else
{
lblMessage.Text = $”上传失败:{result.ErrorMessage}”;
lblMessage.ForeColor = System.Drawing.Color.Red;
}
}
八、ASP.NET Core 中的文件上传
8.1 ASP.NET Core MVC 文件上传
// Controller
[ApiController]
[Route(“api/[controller]”)]
public class FilesController : ControllerBase
{
private readonly IWebHostEnvironment _environment;
private readonly IConfiguration _configuration;
public FilesController(IWebHostEnvironment environment, IConfiguration configuration)
{
_environment = environment;
_configuration = configuration;
}
[HttpPost(“upload”)]
public async Task
{
if (file == null || file.Length == 0)
return BadRequest(“没有文件”);
// 验证文件大小
var maxSize = _configuration.GetValue
if (file.Length > maxSize)
return BadRequest($”文件大小不能超过 {maxSize / (1024 * 1024)}MB”);
// 验证文件类型
var allowedExtensions = _configuration.GetSection(“FileUpload:AllowedExtensions”).Get
var extension = Path.GetExtension(file.FileName).ToLower();
if (!allowedExtensions.Contains(extension))
return BadRequest($”不支持的文件类型”);
// 生成唯一文件名
var uniqueName = $”{Guid.NewGuid()}{extension}”;
var uploadPath = Path.Combine(_environment.WebRootPath, “uploads”);
if (!Directory.Exists(uploadPath))
Directory.CreateDirectory(uploadPath);
// 保存文件
var filePath = Path.Combine(uploadPath, uniqueName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok(new { fileName = uniqueName, originalName = file.FileName });
}
[HttpPost(“upload-multiple”)]
public async Task
{
var results = new List
foreach (var file in files)
{
var result = await UploadFile(file);
if (result is OkObjectResult okResult)
{
results.Add(okResult.Value);
}
}
return Ok(results);
}
}
8.2 Startup 配置
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// 配置文件上传限制
services.Configure
{
options.MaxRequestBodySize = 50 * 1024 * 1024; // 50MB
});
// 配置自定义选项
services.Configure
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseStaticFiles(); // 启用静态文件服务
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
8.3 appsettings.json 配置
{
“FileUpload”: {
“MaxSize”: 52428800,
“AllowedExtensions”: [“.jpg”, “.jpeg”, “.png”, “.gif”, “.pdf”],
“AllowedMimeTypes”: [“image/jpeg”, “image/png”, “image/gif”, “application/pdf”],
“UploadPath”: “uploads”
},
“Kestrel”: {
“Limits”: {
“MaxRequestBodySize”: 52428800
}
}
}
九、高级安全防护
9.1 防止恶意文件上传
public class MaliciousFileScanner
{
// 检查文件是否包含恶意脚本
public static bool ScanForMaliciousContent(byte[] fileBytes, string extension)
{
string content = Encoding.UTF8.GetString(fileBytes);
// 检查常见恶意脚本模式
string[] maliciousPatterns = {
“