Файл возврата в веб-API ASP.Net Core

Проблема

Я хочу вернуть файл в свой контроллер веб-API ASP.Net, но все мои подходы возвращают 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;
}

Когда я вызываю эту конечную точку в своем браузере, веб-API возвращает HttpResponseMessage как JSON с заголовком содержимого HTTP, установленным на application/json.


person Jan Kruse    schedule 25.02.2017    source источник


Ответы (3)


Если это ASP.net-Core, значит, вы смешиваете версии веб-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, поток будет удален до отправки ответа, что приведет к исключению или повреждению ответа.

person Nkosi    schedule 25.02.2017
comment
В моем случае мне нужно было отобразить Excel в памяти и вернуть его для загрузки, поэтому мне нужно было также определить имя файла с расширением: return File(stream, "application/octet-stream", "filename.xlsx"); Таким образом, когда он загружает, пользователь может открыть его напрямую. - person KMJungersen; 22.01.2019
comment
Я понимаю, что в конечном итоге делает NotFound(), но находится ли он в .NET Core или является чем-то локальным для вашего проекта? - person ΩmegaMan; 30.03.2019
comment
@ ΩmegaMan - это вспомогательный метод на ControllerBase, который является частью самого фреймворка docs.microsoft.com/en-us/dotnet/api/ - person Nkosi; 30.03.2019
comment
Хорошо, обнаружена моя проблема, хотя мой контроллер работал в .NET Core 2.2, он не был производным от базового класса Controller и поэтому не имел доступа к методу ControllerBase.NotFound(). После получения все заработало. lol / спасибо - person ΩmegaMan; 30.03.2019
comment
@Nkosi каким-либо способом передать полученный файл с URL-адреса и передать его клиенту кусками, как мы получаем из источника? stackoverflow.com/questions/62476452/ - person user1066231; 19.06.2020
comment
@ user1066231 взгляните на этот docs.microsoft.com/en-us/aspnet/core/release-notes/ сфокусироваться на заголовке диапазона - person Nkosi; 20.06.2020
comment
Вам здесь не нужен оператор using? - person Rob L; 16.11.2020
comment
@RobL не в этом случае. структура избавится от потока, когда ответ будет завершен. Если вы используете оператор using, поток будет удален до отправки ответа. - person Nkosi; 16.11.2020
comment
@RobL проверяет исходный код, который фактически записывает поток в ответ. Обратите внимание на инструкцию using, примененную к переданному в потоке источнику .dot.net / # Microsoft.AspNetCore.Mvc.Core / Infrastructure / - person Nkosi; 16.11.2020
comment
Не будет даже компилироваться, потому что не может преобразовать файл (...) в задачу ‹IActionResult›. - person CoderSteve; 24.11.2020
comment
@CoderSteve, вы чего-нибудь ждали в бою? - person Nkosi; 24.11.2020
comment
@CoderSteve, если вы посмотрите на исходный код Вы увидите, что File действительно возвращает IActionResult производный объект. - person Nkosi; 24.11.2020
comment
Магия, лежащая в основе __get_stream_based_on_id_here__, может быть интересной, поскольку общие функции, возвращающие поток файла, не являются асинхронными, тогда как функции, которые являются асинхронными, возвращают только массив байтов и т. Д. Конечно, я мог бы создать другой поток из массива байтов, но мне было интересно, есть ли там это решение только с одним потоком. - person Martin Schneider; 12.12.2020
comment
Спасибо @Nkosi, ваш комментарий спас мне рассудок! Я получал ошибку сервера 500 при возврате File(...). У меня memoryStream неправильно заключили в оператор using. - person Kevin Brydon; 14.01.2021
comment
@KevinBrydon Я рад, что это помогло. Удачного кодирования. - person Nkosi; 14.01.2021

Вы можете вернуть 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: вернуть FileContentResult

    [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
        };
    }
person H. Naeemaei    schedule 26.10.2019
comment
Если в ControllerBase имеется много перегруженных версий _ 2_ помощник, который возвращает любой из них. - person Nkosi; 27.10.2019
comment
Ваш ответ все еще в силе. Так что не расстраивайтесь. Я просто указал на некоторые ресурсы, которые вы можете использовать для подтверждения своего ответа. - person Nkosi; 27.10.2019
comment
Да, это правда. - person H. Naeemaei; 21.02.2020
comment
было бы хорошо, если бы вы могли заменить 'var' на фактические типы .. Так мы сможем легко понять, что это за реальные объекты. i, e, fileBytes - это байт []? - person Renascent; 23.07.2021
comment
Спасибо @Renascent, я изменил ответ - person H. Naeemaei; 28.07.2021

Вот упрощенный пример потоковой передачи файла:

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

Примечание:

Обязательно используйте FileStreamResult из Microsoft.AspNetCore.Mvc, а не из System.Web.Mvc.

person gpresland    schedule 18.11.2019