Возrotation двоичного файла из controllerа в ASP.NET Web API
Я работаю над веб-сервисом, используя новый WebAPI ASP.NET MVC, который будет обслуживать двоичные файлы, в основном файлы .cab
и .exe
.
Кажется, что работает следующий метод controllerа, что означает, что он возвращает файл, но он устанавливает тип содержимого для application/json
:
public HttpResponseMessage Post(string version, string environment, string filetype) { var path = @"C:\Temp\test.exe"; var stream = new FileStream(path, FileMode.Open); return new HttpResponseMessage(stream, new MediaTypeHeaderValue("application/octet-stream")); }
Есть лучший способ сделать это?
- Постоянный параметр всегда равен нулю.
- Dot character '.' в MVC Web API 2 для запроса, например api / people / STAFF.45287
- Возврат кода статуса http из Web Api controller
- Использование ADAL C # в качестве конфиденциального пользователя / сервера Daemon / Server-to-Server - 401 Unauthorized
- FromBody не привязывает строковый параметр
- Как установить тайм-аут запроса для одного действия controllerа в приложении asp.net mvc
- Тип провайдера CodeDom «Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider» не может быть расположен
- Сериализовать контейнер перечислений в виде строк с помощью JSON.net
- Как передать данные json POST в метод Web API как объект?
- Включить CORS в Web API 2
- Сложный тип получает значение null в параметре ApiController
- Как поддерживать HTTP OPTIONS-глагол в приложении ASP.NET MVC / WebAPI
- Обнаружено, что
Попробуйте использовать простой 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 по-прежнему должен иметь доступ к нему, когда он обрабатывает результат метода controllerа, чтобы отправить данные обратно клиенту. Поэтому не используйте блокusing (var stream = …)
. Web API предоставит вам stream. -
Убедитесь, что stream имеет текущую позицию, установленную в 0 (т.е. начало данных streamа). В приведенном выше примере это заданное, поскольку вы только что открыли файл. Однако в других сценариях (например, когда вы сначала записываете некоторые двоичные данные в
MemoryStream
), убедитесь, чтоstream.Seek(0, SeekOrigin.Begin);
или установитьstream.Position = 0;
-
С файловыми streamами явное указание разрешения
FileAccess.Read
может помочь предотвратить проблемы с правами доступа на веб-серверах; Учетным пулам приложений IIS часто присваиваются только права доступа на чтение / список / выполнение для wwwroot.
Для Web API 2 вы можете реализовать IHttpActionResult
. Вот мой:
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 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); } }
Тогда что-то вроде этого в вашем controllerе:
[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 игнорировать запросы с расширением, чтобы запрос передал его controllerу:
Хотя предлагаемое решение работает нормально, есть еще один способ вернуть массив байтов из controllerа, при этом корректно отформатированный stream ответов:
- В запросе установите заголовок «Accept: application / octet-stream».
- На стороне сервера добавьте форматировщик типа мультимедиа для поддержки этого типа mime.
К сожалению, WebApi не содержит форматера для «application / octet-stream». Существует реализация здесь на GitHub: BinaryMediaTypeFormatter (есть небольшие адаптации, чтобы заставить его работать для webapi 2, изменения в методах изменены).
Вы можете добавить этот форматтер в свою глобальную конфигурацию:
HttpConfiguration config; // ... config.Formatters.Add(new BinaryMediaTypeFormatter(false));
WebApi теперь должен использовать BinaryMediaTypeFormatter
если запрос указывает правильный заголовок Accept.
Я предпочитаю это решение, потому что controller возврата возвращаемого байта [] более удобен для тестирования. Хотя другое решение позволяет вам больше контролировать, если вы хотите вернуть другой тип контента, чем «application / octet-stream» (например, «image / gif»).
Перегрузка, которую вы используете, задает перечисление форматов сериализации. Вам нужно указать тип содержимого явно:
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Для тех, у кого проблема API, вызываемая более одного раза при загрузке довольно большого файла с использованием метода в принятом ответе, настройте буферизацию ответа на истинный System.Web.HttpContext.Current.Response.Buffer = true;
Это гарантирует, что весь бинарный контент буферизуется на стороне сервера, прежде чем он будет отправлен клиенту. В противном случае вы увидите, что на controller будет отправлено несколько запросов, и если вы не обработаете его должным образом, файл станет поврежденным.
Вы можете попробовать
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");