Я работаю над веб-сервисом, используя новый 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"));
}
Есть ли лучший способ сделать это?
Попробуйте использовать простой 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 = …)
. Web 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"/>
В то время как предлагаемое решение работает нормально, есть еще один способ вернуть массив байтов из контроллера, при этом правильно отредактированный поток ответов:
К сожалению, WebApi не включает форматтера для "application/octet-stream". В GitHub есть реализация: BinaryMediaTypeFormatter (есть небольшие адаптации, чтобы заставить его работать для webapi 2, изменены сигнатуры методов).
Вы можете добавить этот форматтер в свою глобальную конфигурацию:
HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));
Теперь WebApi должен использовать BinaryMediaTypeFormatter
, если запрос указывает правильный заголовок Accept.
Я предпочитаю это решение, потому что контроллер возврата возвращаемого байта [] более удобен для тестирования. Хотя другое решение позволяет вам больше контролировать, если вы хотите вернуть другой тип контента, чем "application/octet-stream" (например, "image/gif" ).
Для тех, у кого проблема API, вызываемая более одного раза при загрузке довольно большого файла с использованием метода в принятом ответе, установите для буферизации ответа значение true System.Web.HttpContext.Current.Response.Buffer = true;
Это гарантирует, что весь двоичный контент буферизуется на стороне сервера, прежде чем он будет отправлен клиенту. В противном случае вы увидите, что к контроллеру отправляется несколько запросов, и если вы не будете их обрабатывать должным образом, файл будет поврежден.
Buffer
устарело в пользу BufferOutput
. По умолчанию это true
.
Перегрузка, которую вы используете, задает перечисление форматов сериализации. Вам нужно указать тип содержимого явно:
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Content Type: application/json
в Fiddler. Content Type
по-видимому, установлен правильно, если я httpResponseMessage
перед возвратом ответа httpResponseMessage
. Есть еще идеи?
Вы можете попробовать
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");
Для тех, кто использует .NET Core, вы можете использовать интерфейс IActionResult, например:
[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