在ASP.Net Core Web API中返回文件

Jan*_*use 98 c# .net-core asp.net-core asp.net-core-webapi

问题

我想在我的ASP.Net Web API Controller中返回一个文件,但我的所有方法都返回HttpResponseMessage为JSON.

代码到目前为止

public async Task<HttpResponseMessage> DownloadAsync(string id)
{
    var response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = new StreamContent({{__insert_stream_here__}});
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    return response;
}
Run Code Online (Sandbox Code Playgroud)

当我在浏览器中调用此端点时,Web API会返回设置HttpResponseMessage为HTTP Content Header 的as JSON application/json.

Nko*_*osi 182

如果这是ASP.net-Core,那么您正在混合Web API版本.让操作返回派生,IActionResult因为在当前代码中框架将HttpResponseMessage作为模型处理.

[Route("api/[controller]")]
public class DownloadController : Controller {
    //GET api/download/12345abc
    [HttpGet("{id}"]
    public async Task<IActionResult> Download(string id) {
        Stream stream = await {{__get_stream_based_on_id_here__}}

        if(stream == null)
            return NotFound(); // returns a NotFoundResult with Status404NotFound response.

        return File(stream, "application/octet-stream"); // returns a FileStreamResult
    }    
}
Run Code Online (Sandbox Code Playgroud)

  • 就我而言,我需要在内存中渲染一个Excel并将其返回以供下载,因此我还需要定义一个带有扩展名的文件名:`return File(stream,“ application / octet-stream”,“ filename.xlsx”) ;`这样,用户下载时就可以直接打开它。 (7认同)
  • @RobL 不在这种情况下。响应完成后,框架将处理该流。如果您使用“using”语句,则流将在发送响应之前被处理。 (6认同)
  • 好的,发现 *我的* 问题,虽然我的控制器在 .NET Core 2.2 中工作,但它不是从基类 `Controller` 派生的,因此无法访问 `ControllerBase.NotFound()` 方法。一旦派生,一切都奏效了。大声笑/谢谢 (4认同)
  • `__get_stream_based_on_id_here__` 背后的魔力可能很有趣,因为返回文件流的常见函数不是异步的,而异步函数仅返回字节数组等。Ofc,我可以从字节数组创建另一个流,但我想知道如果有一种只有一个 Stream 的解决方案。 (4认同)
  • @ΩmegaMan它是`ControllerBase`上的辅助方法,并且是框架本身的一部分https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.notfound?view=aspnetcore- 2.2 (2认同)
  • @RobL 检查实际将流写入响应的源代码。请注意应用于传入流的“using”语句https://source.dot.net/#Microsoft.AspNetCore.Mvc.Core/Infrastruct/FileResultExecutorBase.cs,398 (2认同)
  • 谢谢@Nkosi,你的评论拯救了我的理智!返回 `File(...)` 时,我收到 500 服务器错误。我将 memoryStream 错误地包装在 using 语句中。 (2认同)
  • @KurtisJungersen 你不知道你的评论拯救了多少生命!只有在给出文件名后我才能让它为我工作。谢谢你! (2认同)

Ham*_*aei 31

您可以使用以下方法返回 FileResult:

1:返回 FileStreamResult

    [HttpGet("get-file-stream/{id}"]
    public async Task<FileStreamResult> DownloadAsync(string id)
    {
        var fileName="myfileName.txt";
        var mimeType="application/...."; 
        Stream stream = await GetFileStreamById(id);

        return new FileStreamResult(stream, mimeType)
        {
            FileDownloadName = fileName
        };
    }
Run Code Online (Sandbox Code Playgroud)

2:返回文件内容结果

    [HttpGet("get-file-content/{id}"]
    public async Task<FileContentResult> DownloadAsync(string id)
    {
        var fileName="myfileName.txt";
        var mimeType="application/...."; 
        byte[] fileBytes = await GetFileBytesById(id);

        return new FileContentResult(fileBytes, mimeType)
        {
            FileDownloadName = fileName
        };
    }
Run Code Online (Sandbox Code Playgroud)

  • 如果在`ControllerBase`中有许多重载版本的[`ControllerBase.File`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.file?view= aspnetcore-2.2) 返回其中任何一个的帮助程序。 (3认同)
  • 你的回答仍然有效。所以不要感到沮丧。我只是指出一些您可以用来支持您的答案的资源。 (2认同)

gpr*_*and 21

这是流式传输文件的简单示例:

using System.IO;
using Microsoft.AspNetCore.Mvc;
Run Code Online (Sandbox Code Playgroud)
[HttpGet("{id}")]
public async Task<FileStreamResult> Download(int id)
{
    var path = "<Get the file path using the ID>";
    var stream = File.OpenRead(path);
    return new FileStreamResult(stream, "application/octet-stream");
}
Run Code Online (Sandbox Code Playgroud)

笔记:

一定要使用FileStreamResultfromMicrosoft.AspNetCore.Mvc不是from System.Web.Mvc


小智 8

ASP.NET 5 WEB API 和 Angular 12

您可以从服务器返回 FileContentResult 对象 (Blob)。它不会自动下载。您可以通过编程方式在前端应用程序中创建锚标记,并将 href 属性设置为通过以下方法从 Blob 创建的对象 URL。现在单击锚点将下载该文件。您也可以通过将“下载”属性设置为锚点来设置文件名。

downloadFile(path: string): Observable<any> {
        return this._httpClient.post(`${environment.ApiRoot}/accountVerification/downloadFile`, { path: path }, {
            observe: 'response',
            responseType: 'blob'
        });
    }

saveFile(path: string, fileName: string): void {
            this._accountApprovalsService.downloadFile(path).pipe(
                take(1)
            ).subscribe((resp) => {
                let downloadLink = document.createElement('a');
                downloadLink.href = window.URL.createObjectURL(resp.body);
                downloadLink.setAttribute('download', fileName);
                document.body.appendChild(downloadLink);
                downloadLink.click();
                downloadLink.remove();
            });
            
        }
Run Code Online (Sandbox Code Playgroud)

后端

[HttpPost]
[Authorize(Roles = "SystemAdmin, SystemUser")]
public async Task<IActionResult> DownloadFile(FilePath model)
{
    if (ModelState.IsValid)
    {
        try
        {
            var fileName = System.IO.Path.GetFileName(model.Path);
            var content = await System.IO.File.ReadAllBytesAsync(model.Path);
            new FileExtensionContentTypeProvider()
                .TryGetContentType(fileName, out string contentType);
            return File(content, contentType, fileName);
        }
        catch
        {
            return BadRequest();
        }
    }

    return BadRequest();

}
Run Code Online (Sandbox Code Playgroud)