Blazor Server中MultipartFormDataContent在Razor页面失效但Swagger可用
Blazor Server文件上传API接收不到文件问题解决
问题描述
在.NET 7.0的Blazor Server项目中,参考微软官方文档实现文件上传功能,API在Swagger中可正常接收文件并完成上传,但通过Razor页面调用时,API控制器中的files.Count始终为0,无法获取传输的文件。
相关代码
Razor页面代码
@page "/file-upload-2" @using System.Linq @using System.Net.Http.Headers @using Dostware.PromoPazarV1.Files @using Dostware.PromoPazarV1.Models @using Dostware.PromoPazarV1.Models.UploadResult @using Microsoft.Extensions.Logging @using System.Net.Http.Json @inject HttpClient Http @inject ILogger<ImageUpload> Logger @inject FileUploadAppService FileUploadAppService <PageTitle>File Upload 2</PageTitle> <h1>File Upload Example 2</h1> <p> <label> Upload up to @maxAllowedFiles files: <InputFile OnChange="OnInputFileChange" multiple /> </label> </p> @if (files.Count > 0) { <div class="card"> <div class="card-body"> <ul> @foreach (var file in files) { <li> File: @file.Name <br> @if (FileUpload(uploadResults, file.Name, Logger, out var result)) { <span> Stored File Name: @result.StoredFileName </span> } else { <span> There was an error uploading the file (Error: @result.ErrorCode). </span> } </li> } </ul> </div> </div> } @code { private List<File> files = new(); private List<UploadResult> uploadResults = new(); private int maxAllowedFiles = 3; private bool shouldRender; protected override bool ShouldRender() => shouldRender; private async Task OnInputFileChange(InputFileChangeEventArgs e) { shouldRender = false; long maxFileSize = 1024 * 100000; var upload = false; using var content = new MultipartFormDataContent(); foreach (var file in e.GetMultipleFiles(maxAllowedFiles)) { if (uploadResults.SingleOrDefault( f => f.FileName == file.Name) is null) { try { files.Add(new() { Name = file.Name }); var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType); content.Add( content: fileContent, name: "\"files\"", fileName: file.Name); upload = true; } catch (Exception ex) { Logger.LogInformation( "{FileName} not uploaded (Err: 6): {Message}", file.Name, ex.Message); uploadResults.Add( new() { FileName = file.Name, ErrorCode = 6, Uploaded = false }); } } } if (upload) { var response = await FileUploadAppService.PostFile(content); } shouldRender = true; } private static bool FileUpload(IList<UploadResult> uploadResults, string? fileName, ILogger<ImageUpload> logger, out UploadResult result) { result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new(); if (!result.Uploaded) { logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName); result.ErrorCode = 5; } return result.Uploaded; } private class File { public string? Name { get; set; } } }
API控制器代码
[HttpPost("PostFile")] public async Task<ActionResult<IList<UploadResult>>> PostFile( [FromForm] IEnumerable<IFormFile> files) { var maxAllowedFiles = 3; long maxFileSize = 1024 * 500000; var filesProcessed = 0; var resourcePath = new Uri($"{Request.Scheme}://{Request.Host}/"); List<UploadResult> uploadResults = new(); foreach (var file in files) { var uploadResult = new UploadResult(); string trustedFileNameForFileStorage; var untrustedFileName = file.FileName; uploadResult.FileName = untrustedFileName; var trustedFileNameForDisplay = WebUtility.HtmlEncode(untrustedFileName); if (filesProcessed < maxAllowedFiles) { if (file.Length == 0) { logger.LogInformation("{FileName} length is 0 (Err: 1)", trustedFileNameForDisplay); uploadResult.ErrorCode = 1; } else if (file.Length > maxFileSize) { logger.LogInformation("{FileName} of {Length} bytes is " + "larger than the limit of {Limit} bytes (Err: 2)", trustedFileNameForDisplay, file.Length, maxFileSize); uploadResult.ErrorCode = 2; } else { try { trustedFileNameForFileStorage = Path.GetRandomFileName(); var originalFileName = file.FileName; var fileExtension = Path.GetExtension(originalFileName); var newFileName = trustedFileNameForFileStorage + fileExtension; var path = Path.Combine(env.ContentRootPath, "wwwroot", "uploads", newFileName); await using FileStream fs = new(path, FileMode.Create); await file.CopyToAsync(fs); logger.LogInformation("{FileName} saved at {Path}", trustedFileNameForDisplay, path); uploadResult.Uploaded = true; uploadResult.StoredFileName = trustedFileNameForFileStorage; } catch (IOException ex) { logger.LogError("{FileName} error on upload (Err: 3): {Message}", trustedFileNameForDisplay, ex.Message); uploadResult.ErrorCode = 3; } } filesProcessed++; } else { logger.LogInformation("{FileName} not uploaded because the " + "request exceeded the allowed {Count} of files (Err: 4)", trustedFileNameForDisplay, maxAllowedFiles); uploadResult.ErrorCode = 4; } uploadResults.Add(uploadResult); } return new CreatedResult(resourcePath, uploadResults); }
服务层代码
public async Task<ActionResult<IList<UploadResult>>> PostFile(MultipartFormDataContent multipartFormDataContent) { try { try { var response = _http.PostAsync("FileSave/PostFile", multipartFormDataContent); } catch (Exception ex) { var den = ex.Message; } } catch (Exception ex) { var den = ex.Message; } return new CreatedResult("", uploadResults); }
解决方案
- 修正MultipartFormDataContent参数名格式:Razor页面中
content.Add方法的name参数带多余双引号,导致控制器无法识别。将name: "\"files\""改为name: "files"。 - 等待异步请求完成:服务层的
PostAsync未用await会导致请求未完成就返回,修正为var response = await _http.PostAsync("FileSave/PostFile", multipartFormDataContent);,并添加响应处理逻辑(比如读取返回的上传结果)。 - 配置HttpClient BaseAddress:确保服务层的
_http实例配置了正确的API基础地址,可在Program.cs中通过AddHttpClient注入时指定。 - 可选:手动设置边界符:若上述修改无效,可手动指定MultipartFormDataContent的边界符,避免自动生成的边界符解析异常:
var boundary = $"----{Guid.NewGuid():N}"; using var content = new MultipartFormDataContent(boundary);
内容的提问来源于stack exchange,提问作者Ali Bircan
相关产品推荐
相关产品推荐

