ChatGPT解决这个技术问题 Extra ChatGPT

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

问题

我想在我的 ASP.Net Web API 控制器中返回一个文件,但我所有的方法都将 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;
}

当我在浏览器中调用此端点时,Web API 以 JSON 形式返回 HttpResponseMessage,并将 HTTP 内容标头设置为 application/json


N
Nkosi

如果这是 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
    }    
}

笔记:

当响应完成时,框架将处理在这种情况下使用的流。如果使用 using 语句,则流将在响应发送之前被释放,并导致异常或损坏的响应。


在我的情况下,我需要在内存中呈现 Excel 并将其返回以供下载,因此我还需要定义一个带扩展名的文件名:return File(stream, "application/octet-stream", "filename.xlsx"); 这样下载时用户可以直接打开它。
@ΩmegaMan 它是 ControllerBase 上的辅助方法,是框架本身的一部分 docs.microsoft.com/en-us/dotnet/api/…
好的,发现 my 问题,虽然我的控制器在 .NET Core 2.2 中工作,但它不是从基类 Controller 派生的,因此无法访问 ControllerBase.NotFound() 方法。一旦派生,一切都奏效了。大声笑/谢谢
@RobL 在这种情况下不是。响应完成后,框架将处理流。如果您使用 using 语句,则将在发送响应之前释放流。
__get_stream_based_on_id_here__ 背后的魔力可能很有趣,因为返回文件流的普通函数不是异步的,而异步函数只返回字节数组等。Ofc,我可以从字节数组创建另一个流,但我想知道如果只有一个流的解决方案。
H
Hamed Naeemaei

您可以使用以下方法返回 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
        };
    }

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
        };
    }

如果在 ControllerBase 中有许多 ControllerBase.File 帮助器的重载版本,它们会返回其中任何一个。
你的回答仍然有效。所以不要心灰意冷。我只是指出一些您可以用来支持您的答案的资源。
是的,这是真的。
使用 FileStreamResult 将帮助您控制服务器内存消耗。由于您没有将整个大文件放入内存,而是将其流式传输。
g
gpresland

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

using System.IO;
using Microsoft.AspNetCore.Mvc;
[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");
}

笔记:

请务必使用 Microsoft.AspNetCore.Mvc 中的 FileStreamResultSystem.Web.Mvc 中的not


T
Tanvir

ASP.NET 5 网络 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();
            });
            
        }

后端

[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();

}

为什么要将文件路径从前端传递到后端
假设有一个按文件名列出上传的用户文档的页面,每个列表项(文档)都有一个下载按钮,后端是WEB API。
您将传递名称而不是路径:上传路径、名称或 id 下载
是的,ID 是建议通过的字段。该代码没有被重构。
A
Adeel Ahmed

以下是 .NET Core Web API 中返回文件(例如图像文件)的基本示例:

<img src="@Url.Action("RenderImage", new { id = id})" alt="No Image found" />

下面是从控制器返回文件到视图的代码。以下是将返回文件的 Action 方法:

    [Route("api/[controller]")]
    public class DownloadController : Controller
    {
        //GET api/download/123
        [HttpGet]
        public async Task<IActionResult> RenderImage(string userId)
        {
            //get Image file using _fileservice from db
            var result = await _fileService.getFile(userId);

            if (result.byteStream == null)
                return NotFound();

            return File(result.byteStream, result.ContentType, result.FileName);
        }
    }

笔记:

我们的文件应首先转换为 byte[],然后以 varbinary(max) 的形式保存在数据库中以便检索。


y
yww325

FileStreamResult 对我有用。并且 File 不是 IActionResult。我不知道它是如何工作的。