如何在ASP.NET Core 6最小化API中进行文件上传?

8

我想在ASP.NET Core 6中创建一个简单的文件上传端点,并认为它应该像这篇文章描述的那样容易:https://dotnetthoughts.net/handling-file-uploads-in-openapi-with-aspnet-core/

当我定义了一个端点,例如:

app.MapPost("/upload", (IFormFile file) =>
{
    //Do something with the file
    return Results.Ok();
}).Accepts<IFormFile>("multipart/form-data").Produces(200);


当我调用端点时,返回了415错误。我收到的消息大致是:期望支持JSON格式的媒体类型,但实际接收到的是"multipart/form-data; ..."。不确定为什么会期望支持JSON格式,当我说该端点应该接受“multipart/form-data”。这里有什么想法或建议吗?
4个回答

8

目前,Minimal APIs 中的绑定支持是相当有限的。支持的绑定源包括:

  • 路由值
  • 查询字符串
  • 标头
  • 正文(作为 JSON)
  • 依赖注入提供的服务
  • 自定义

注意:.NET 6 不支持从表单中进行绑定。

您可以利用自定义绑定或使用特殊类型处理

app.MapPost("/upload", (HttpRequest request) =>
{
    //Do something with the file
    var files = request.Form.Files;
    return Results.Ok();
})
.Accepts("multipart/form-data")
.Produces(200);

更新

由于的 Minimal APIs 应该能够直接绑定 IFormFile/IFormFileCollection

app.MapPost("/upload", async (IFormFile file) =>
{
    // ...
});

app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        // ...
    }
});

谢谢。明天会测试。我只是想知道他们在我参考的博客上是如何做到的?嗯 - Tomas Jansson
@TomasJansson 我非常确定他们没有使用 Minimal APIs,而是使用“标准”的Web APIs - Guru Stron
1
你验证过这个代码确实可行吗?当我尝试运行时,出现了“意外的流结尾,内容可能已经被其他组件读取”的异常。一个区别是我使用了Accepts<IFormFile>("multipart/form-data"),我猜这就是为什么asp.net尝试读取body的原因。但是我似乎没有非泛型的Accepts可以使用。 - Tomas Jansson
1
没事了,我想问题可能出在我使用 VS Code REST 客户端创建请求的方式上,当我从 Insomnia 测试端点时,它可以正常工作。 - Tomas Jansson

8

在此仅作记录,你可以使用以下示例代码上传任何ContentType类型的文件。

在此示例中,我选择了“text/plain”ContentType类型,但您可以通过编辑.Accepts<IFormFile>("text/plain");行选择自己想要的ContentType。

app.MapPost("/upload",
    async (HttpRequest request) =>
    {
        using (var reader = new StreamReader(request.Body, System.Text.Encoding.UTF8))
        {

            // Read the raw file as a `string`.
            string fileContent = await reader.ReadToEndAsync();

            // Do something with `fileContent`...
    
            return "File Was Processed Sucessfully!";
        }
    }).Accepts<IFormFile>("text/plain");

Swagger UI对此也有很好的支持:

在这里输入图片描述


如果文件是二进制的话,我认为最好通过 request.Form.Files[0] 访问文件,而不是从主体中读取。虽然我还没有测试过。 - Tono Nam

2
这个问题已经在.NET 7中得到解决。现在支持IFormFile和IFormFileCollection。您应该能够在MediatR管道中使用它。
参考:.NET 7最小API支持文件上传绑定
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.MapPost("/upload", async (IFormFile file) =>
{
    var tempFile = Path.GetTempFileName();
    app.Logger.LogInformation(tempFile);
    using var stream = File.OpenWrite(tempFile);
    await file.CopyToAsync(stream);
});

app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        var tempFile = Path.GetTempFileName();
        app.Logger.LogInformation(tempFile);
        using var stream = File.OpenWrite(tempFile);
        await file.CopyToAsync(stream);
    }
});

app.Run();

0
app.MapPost("/upload",
    async Task<IResult> (HttpRequest request) =>
    {
        if (!request.HasFormContentType)
            return Results.BadRequest();

        var form = await request.ReadFormAsync();

        if (form.Files.Any() == false)
            return Results.BadRequest("There are no files");

        var file = form.Files.FirstOrDefault();

        if (file is null || file.Length == 0)
            return Results.BadRequest("File cannot be empty");

        using var stream = file.OpenReadStream();

        var reader = new StreamReader(stream);
        var text = await reader.ReadToEndAsync();

        return Results.Ok(text);
    }).Accepts<IFormFile>("multipart/form-data"); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接