You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebApi多文件上传:如何验证文件是否成功上传至服务器

多文件上传API的验证与异常修复方案

问题根源分析

  1. 未提供对应文件Key时,List<IFormFile>会被绑定为空集合,现有代码遍历空集合后直接返回"Success",未做空值校验
  2. 异常捕获仅在WriteFile内部,且返回字符串而非HTTP错误状态码,导致控制器始终返回200
  3. 缺少单个文件的有效性验证(比如空文件、非法格式、过大文件等)

分步修复方案

1. 添加入口参数校验

在UploadFile方法开头先判断是否有文件传入,直接返回400错误:

[HttpPost]
[Route("/api/FileUpload")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UploadFile(List<IFormFile> files, CancellationToken cancellationToken)
{
    // 先校验是否有文件上传
    if (files == null || !files.Any())
    {
        return BadRequest("未上传任何文件");
    }

    var result = await WriteFile(files);
    return Ok(result);
}

2. 完善单个文件的有效性验证

在WriteFile的循环中添加文件合法性校验,提前拦截无效文件:

private async Task<string> WriteFile(List<IFormFile> files)
{
    var uploadDir = Path.Combine(Directory.GetCurrentDirectory(), "UploadedFiles");
    if (!Directory.Exists(uploadDir))
    {
        Directory.CreateDirectory(uploadDir);
    }

    try
    {
        foreach (var file in files)
        {
            // 校验文件是否为空
            if (file == null || file.Length == 0)
            {
                return $"文件[{file.FileName}]为空,上传失败";
            }

            // 可选:限制文件大小(示例:最大10MB)
            if (file.Length > 10 * 1024 * 1024)
            {
                return $"文件[{file.FileName}]过大,最大支持10MB";
            }

            // 可选:校验文件扩展名(示例:仅允许图片和PDF)
            var allowedExts = new[] { ".jpg", ".png", ".pdf" };
            var ext = Path.GetExtension(file.FileName).ToLower();
            if (!allowedExts.Contains(ext))
            {
                return $"文件[{file.FileName}]格式不允许,仅支持{string.Join(",", allowedExts)}";
            }

            // 避免文件名重复,用时间戳前缀重命名
            var safeFileName = $"{DateTime.Now.Ticks}_{Path.GetFileName(file.FileName)}";
            var exactPath = Path.Combine(uploadDir, safeFileName);

            using (var stream = new FileStream(exactPath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }
        }
        return "文件上传成功";
    }
    catch (Exception ex)
    {
        // 这里可以添加日志记录
        return $"文件上传失败:{ex.Message}";
    }
}

3. 优化异常与状态码返回

如果希望更规范地返回HTTP状态码,建议把WriteFile改成返回结果对象,由控制器统一处理:

// 定义上传结果类
public class UploadResult
{
    public bool IsSuccess { get; set; }
    public string Message { get; set; }
}

// 改写WriteFile方法
private async Task<UploadResult> WriteFile(List<IFormFile> files)
{
    var uploadDir = Path.Combine(Directory.GetCurrentDirectory(), "UploadedFiles");
    if (!Directory.Exists(uploadDir))
    {
        Directory.CreateDirectory(uploadDir);
    }

    try
    {
        foreach (var file in files)
        {
            if (file.Length == 0)
            {
                return new UploadResult { IsSuccess = false, Message = $"文件[{file.FileName}]为空" };
            }

            var safeFileName = $"{DateTime.Now.Ticks}_{Path.GetFileName(file.FileName)}";
            var exactPath = Path.Combine(uploadDir, safeFileName);

            using (var stream = new FileStream(exactPath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }
        }
        return new UploadResult { IsSuccess = true, Message = "文件上传成功" };
    }
    catch (Exception ex)
    {
        return new UploadResult { IsSuccess = false, Message = $"上传失败:{ex.Message}" };
    }
}

// 控制器方法中根据结果返回对应状态码
public async Task<IActionResult> UploadFile(List<IFormFile> files, CancellationToken cancellationToken)
{
    if (files == null || !files.Any())
    {
        return BadRequest("未上传任何文件");
    }

    var result = await WriteFile(files);
    return result.IsSuccess ? Ok(result.Message) : BadRequest(result.Message);
}

额外建议

  • 避免直接使用原文件名,防止文件覆盖和路径遍历攻击
  • 添加日志记录,方便排查上传失败的问题
  • 可以使用IWebHostEnvironment获取wwwroot路径替代Directory.GetCurrentDirectory(),更符合ASP.NET Core规范

内容的提问来源于stack exchange,提问作者Jerry Trac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 04:37:50