Возврат двоичного файла из контроллера в ASP.NET Web API
Я работаю над веб-сервисом, использующим новый WebAPI ASP.NET MVC, который будет обслуживать двоичные файлы, в основном .cab
а также .exe
файлы.
Следующий метод контроллера, кажется, работает, это означает, что он возвращает файл, но он устанавливает тип содержимого в application/json
:
public HttpResponseMessage<Stream> Post(string version, string environment, string filetype)
{
var path = @"C:\Temp\test.exe";
var stream = new FileStream(path, FileMode.Open);
return new HttpResponseMessage<Stream>(stream, new MediaTypeHeaderValue("application/octet-stream"));
}
Есть лучший способ сделать это?
6 ответов
Попробуйте использовать простой HttpResponseMessage
с этими Content
свойство установлено в StreamContent
:
// using System.IO;
// using System.Net.Http;
// using System.Net.Http.Headers;
public HttpResponseMessage Post(string version, string environment,
string filetype)
{
var path = @"C:\Temp\test.exe";
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
Несколько замечаний о stream
используемый:
Вы не должны звонить
stream.Dispose()
поскольку веб-API все еще должен иметь возможность доступа к нему при обработке метода контроллераresult
отправить данные обратно клиенту. Поэтому не используйтеusing (var stream = …)
блок. Веб-API будет распоряжаться потоком для вас.Убедитесь, что текущая позиция потока установлена в 0 (то есть начало данных потока). В приведенном выше примере это дано, поскольку вы только что открыли файл. Однако в других сценариях (например, когда вы впервые записываете двоичные данные в
MemoryStream
), убедись вstream.Seek(0, SeekOrigin.Begin);
или установитьstream.Position = 0;
С файловыми потоками, явно указав
FileAccess.Read
разрешение может помочь предотвратить проблемы с правами доступа на веб-серверах; Учетным записям пула приложений IIS часто предоставляются только права на чтение / просмотр / выполнение доступа к wwwroot.
Для Web API 2 вы можете реализовать IHttpActionResult
, Вот мой:
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
class FileResult : IHttpActionResult
{
private readonly string _filePath;
private readonly string _contentType;
public FileResult(string filePath, string contentType = null)
{
if (filePath == null) throw new ArgumentNullException("filePath");
_filePath = filePath;
_contentType = contentType;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(File.OpenRead(_filePath))
};
var contentType = _contentType ?? MimeMapping.GetMimeMapping(Path.GetExtension(_filePath));
response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
return Task.FromResult(response);
}
}
Тогда как-то так в вашем контроллере:
[Route("Images/{*imagePath}")]
public IHttpActionResult GetImage(string imagePath)
{
var serverPath = Path.Combine(_rootPath, imagePath);
var fileInfo = new FileInfo(serverPath);
return !fileInfo.Exists
? (IHttpActionResult) NotFound()
: new FileResult(fileInfo.FullName);
}
И вот один из способов, которым вы можете указать IIS игнорировать запросы с расширением, чтобы запрос направлялся в контроллер:
<!-- web.config -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
Для тех, кто использует.NET Core:
Вы можете использовать интерфейс IActionResult в методе контроллера API, например так...
[HttpGet("GetReportData/{year}")]
public async Task<IActionResult> GetReportData(int year)
{
// Render Excel document in memory and return as Byte[]
Byte[] file = await this._reportDao.RenderReportAsExcel(year);
return File(file, "application/vnd.openxmlformats", "fileName.xlsx");
}
Этот пример упрощен, но должен понять суть. В.NET Core этот процесс намного проще, чем в предыдущих версиях.NET - то есть без настройки типа ответа, содержимого, заголовков и т. Д.
Также, конечно, тип MIME для файла и расширение будут зависеть от индивидуальных потребностей.
Ссылка: ТАК Сообщение от @NKosi
Хотя предлагаемое решение работает нормально, есть другой способ вернуть байтовый массив из контроллера с правильно отформатированным потоком ответов:
- В запросе установите заголовок "Accept: application/octet-stream".
- На стороне сервера добавьте средство форматирования мультимедийного типа для поддержки этого типа MIME.
К сожалению, WebApi не содержит никакого средства форматирования для "application/octet-stream". Здесь есть реализация на GitHub: BinaryMediaTypeFormatter (есть небольшие изменения, чтобы он работал для webapi 2, сигнатуры методов изменены).
Вы можете добавить этот форматер в вашу глобальную конфигурацию:
HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));
WebApi теперь должен использовать BinaryMediaTypeFormatter
если в запросе указан правильный заголовок Accept.
Я предпочитаю это решение, потому что контроллер действия, возвращающий byte[], более удобен для тестирования. Тем не менее, другое решение позволяет вам лучше контролировать, если вы хотите вернуть другой тип контента, чем "application/octet-stream" (например, "image/gif").
Если у вас есть проблема с вызовом API более одного раза при загрузке довольно большого файла с использованием метода в принятом ответе, установите для буферизации ответа значение true System.Web.HttpContext.Current.Response.Buffer = true;
Это гарантирует, что весь двоичный контент буферизируется на стороне сервера перед его отправкой клиенту. В противном случае вы увидите несколько запросов, отправляемых на контроллер, и если вы не обработаете их должным образом, файл будет поврежден.
Используемая вами перегрузка устанавливает перечисление форматировщиков сериализации. Вам необходимо явно указать тип содержимого:
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Вы могли бы попробовать
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");
Вы можете попробовать следующий фрагмент кода
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");
Надеюсь, это сработает для вас.