.NET Web API Blueimp multiple file upload error "Unexpected end of MIME multipart stream. MIME multipart message is not complete." - c#

I am building a video management app that allows for uploading multiple videos to Azure Storage which is then encoded by Azure Media Services.
My issue is that if I upload just 1 file at a time with blueimp, everything works fine. When I add more than one file to the upload, I get the error on the second file.
Unexpected end of MIME multipart stream. MIME multipart message is not complete.
I have read that it could be to the stream missing the end of file terminator, so I added in the suggested tweak to append the line terminator (per this article ASP.NET Web API, unexpected end of MIME multi-part stream when uploading from Flex FileReference) with no luck.
If I post as single files (by either iterating over the files selected for upload) and send them as individual posts, it works. My issue is that I want to select several files as well as add additional metadata and hit one submit button. When I do it this way, the first file uploads, and the second one appears to start to upload but then I get the error 500 "Unexpected end of MIME multipart stream. MIME multipart message is not complete" message.
Here is my upload code (Web API):
[HttpPost]
public async Task<HttpResponseMessage> UploadMedia()
{
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Headers["content-type"] != null)
{
httpRequest.Headers.Remove("content-type");
}
httpRequest.Headers.Add("enctype", "multipart/form-data");
if (httpRequest.Files.Count > 0)
{
var docfiles = new List<string>();
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
string assignedSectionList = string.Empty;
postedFile.SaveAs(filePath);
docfiles.Add(filePath);
string random = Helpers.Helper.RandomDigits(10).ToString();
string ext = System.IO.Path.GetExtension(filePath);
string newFileName = (random + ext).ToLower();
MediaType mediaType = MediaType.Video;
if (newFileName.Contains(".mp3"))
{
mediaType = MediaType.Audio;
}
if (httpRequest.Form["sectionList"] != null)
{
assignedSectionList = httpRequest.Form["sectionList"];
}
MediaUploadQueue mediaUploadQueueItem = new MediaUploadQueue();
mediaUploadQueueItem.OriginalFileName = postedFile.FileName;
mediaUploadQueueItem.FileName = newFileName;
mediaUploadQueueItem.UploadedDateTime = DateTime.UtcNow;
mediaUploadQueueItem.LastUpdatedDateTime = DateTime.UtcNow;
mediaUploadQueueItem.Status = "pending";
mediaUploadQueueItem.Size = postedFile.ContentLength;
mediaUploadQueueItem.Identifier = random;
mediaUploadQueueItem.MediaType = mediaType;
mediaUploadQueueItem.AssignedSectionList = assignedSectionList;
db.MediaUploadQueue.Add(mediaUploadQueueItem);
db.SaveChanges();
byte[] chunk = new byte[httpRequest.ContentLength];
httpRequest.InputStream.Read(chunk, 0, Convert.ToInt32(httpRequest.ContentLength));
var provider = new AzureStorageMultipartFormDataStreamProviderNoMod(new AzureMediaServicesHelper().container);
provider.fileNameOverride = newFileName;
await Request.Content.ReadAsMultipartAsync(provider); //this uploads it to the storage account
//AzureMediaServicesHelper amsHelper = new AzureMediaServicesHelper();
string assetId = amsHelper.CommitAsset(mediaUploadQueueItem); //begin the process of encoding the file
mediaUploadQueueItem.AssetId = assetId;
db.SaveChanges();
////start the encoding
amsHelper.EncodeAsset(assetId);
}
result = Request.CreateResponse(HttpStatusCode.Created, docfiles);
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
return result;
}
Here is the code for the upload handler that sends to Azure Blob storage
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
if (parent == null) throw new ArgumentNullException(nameof(parent));
if (headers == null) throw new ArgumentNullException(nameof(headers));
if (!_supportedMimeTypes.Contains(headers.ContentType.ToString().ToLower()))
{
throw new NotSupportedException("Only jpeg and png are supported");
}
// Generate a new filename for every new blob
var fileName = Guid.NewGuid().ToString();
if (!String.IsNullOrEmpty(fileNameOverride))
fileName = fileNameOverride;
CloudBlockBlob blob = _blobContainer.GetBlockBlobReference(fileName);
if (headers.ContentType != null)
{
// Set appropriate content type for your uploaded file
blob.Properties.ContentType = headers.ContentType.MediaType;
}
this.FileData.Add(new MultipartFileData(headers, blob.Name));
return blob.OpenWrite();
}
Here is the javascript code. The first one is sending the files individual as separate posts, it works.
$("#fileupload").fileupload({
autoUpload: false,
dataType: "json",
add: function (e, data) {
data.context = $('<p class="file">')
.append($('<a target="_blank">').text(data.files[0].name))
.appendTo(document.body);
data.submit();
},
progress: function (e, data) {
var progress = parseInt((data.loaded / data.total) * 100, 10);
data.context.css("background-position-x", 100 - progress + "%");
},
done: function (e, data) {
data.context
.addClass("done")
.find("a")
.prop("href", data.result.files[0].url);
}
});
This code below does not work. It pushes all the files into and array and sends them in one single post. This one fails on the second file. If I upload just one file using this code, it works.
var filesList = new Array();
$(function () {
$('#fileupload').fileupload({
autoUpload: false,
dropZone: $('#dropzone'),
add: function (e, data) {
filesList.push(data.files[0]);
data.context = $('<div class="file"/>', { class: 'thumbnail pull-left' }).appendTo('#files');
var node = $('<p />').append($('<span/>').text(data.files[0].name).data(data));
node.appendTo(data.context);
},
progress: function (e, data) { //Still working on this part
//var progress = parseInt((data.loaded / data.total) * 100, 10);
//data.context.css("background-position-x", 100 - progress + "%");
},
}).on('fileuploadprocessalways', function (e, data) {
var index = data.index,
file = data.files[index],
node = $(data.context.children()[index]);
if (file.preview) {
node.prepend('<br>').prepend(file.preview);
}
if (file.error) {
node.append('<br>').append($('<span class="text-danger"/>').text(file.error));
}
}).prop('disabled', !$.support.fileInput)
.parent().addClass($.support.fileInput ? undefined : 'disabled');
$("#uploadform").submit(function (event) {
if (filesList.length > 0) {
console.log("multi file submit");
event.preventDefault();
$('#fileupload').fileupload('send', { files: filesList })
.success(function (result, textStatus, jqXHR) { console.log('success'); })
.error(function (jqXHR, textStatus, errorThrown) { console.log('error'); })
.complete(function (result, textStatus, jqXHR) {
console.log('complete: ' + JSON.stringify(result)); //The error 500 is returned here. In fiddler, it shows and error 500. If I try to trap in Visual Studio, I can't seem to pinpoint the exception.
// window.location='back to view-page after submit?'
});
} else {
console.log("plain default form submit");
}
});
});
Any thoughts on why this would be happening? I have tried every approach I can think about with no luck. Thank you in advance!

I want to point out that the architecture of your code might cause timeouts or errors.
I would first upload everything to azure storage, storage the status in cache or database.
then I would fire a background job (hangfire, azure functions, webjobs) to process uploading to media service to do the other stuff.
I would suggest doing this asynchronously from the user input.
as per the documentation of dropzone make sure you add name in the HTML tag
<form action="/file-upload" class="dropzone">
<div class="fallback">
<input name="file" type="file" multiple />
</div>
</form>
if you are doing it programatically:
function param() {
return "files";
}
Dropzone.options.myDropzone = {
uploadMultiple: true,
paramName: param,
}
on the backend you need to add \r\n after each stream:

Related

uploading and accessing images into asp .net api folder using angular

I have angular project as my client side and .Net 6 web api project as my backend. I am still new to both technologies. I am creating a website and there is a functionality that I am trying to add and haven't been successful so far. I want to upload images into a .Net web api project images folder using angular. I also want to later access those images from angular project. I want to store the path of the image files in the database. I have tried to check for the code on the internet without success. Your assistance will be appreciated.
First, submit your's files from frontend with FormData
postWithFile(url: string, obj: any, files: File[]) {
let cloneHeader: any = {};
let options: any = {
headers: new HttpHeaders(cloneHeader),
observe: 'response',
responseType: 'json'
};
let formData: FormData = new FormData();
if (typeof obj == 'object') { // obj is external submit data
formData.append('data', JSON.stringify(obj));
} else {
formData.append('data', obj);
}
if (files && files.length > 0) {
files.forEach((ds, index) => {
formData.append('file_' + index, ds, ds.name);
});
}
return this._http
.post(this.host + url, formData, options)
.pipe(map((res: any) => {
return res.body;
}));
}
And backend handle request with HttpContext.Current.Request.Files, save images to server and store path of images in database
[HttpPost]
public ResponseMessage<bool?> UploadImages()
{
var response = new ResponseMessage<bool?>();
try
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
ExternalDataModel model = MessageConvert.DeserializeObject<ExternalDataModel>(HttpContext.Current.Request["data"]); // obj in frontend
//
List<string> listImages = new List<string>();
DateTime now = DateTime.Now;
string buildPath = $"{string.Format("{0:00}", now.Year)}\\{string.Format("{0:00}", now.Month)}\\{string.Format("{0:00}", now.Day)}"; // change by your's folder path
foreach (string file in HttpContext.Current.Request.Files)
{
var fileContent = HttpContext.Current.Request.Files[file];
int fileLength = fileContent.ContentLength;
if (fileContent != null && fileLength > 0)
{
var stream = fileContent.InputStream;
byte[] imgByteArray;
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
imgByteArray = memoryStream.ToArray();
}
string fileName = $"format_file_name_if_need_{fileContent.FileName}";
string RelativeFolder = $"{buildPath}";
string AbsoluteFolder = Path.Combine("FOLDER_IN_SERVER_FULL_PATH", RelativeFolder);
if (!Directory.Exists(AbsoluteFolder))
{
Directory.CreateDirectory(AbsoluteFolder);
}
string pathSave = Path.Combine(RelativeFolder, fileName);
FileHelper.SaveFileFromBinaryArray(pathSave, imgByteArray);
listImages.Add(pathSave);
}
}
// model.listImages = listImages; // assign to model to save to DB
//
// var data = _bus.uploadImage(model);
// if (data)
// {
// response.Data = true;
// response.MessageCode = MessageCodes.UpdateSuccessfully;
// }
}
catch (Exception ex)
{
response.MessageCode = ex.Message;
}
return response;
}

How Angular handle chunked HTTP response?

My ASP.NET API (.NET Core 3.1) application generates an excel file, saves it in local dir, reads like a fileStream and sends it to the client application (Angular 11.2.9). Everythings work fine in the local machine, but these two applications are running in OpenShift. My environment has response size limits and then the excel file is larger than 10 MB Angular starts crashing because it can't read the blob response correctly. To fix this I implemented Chunks in HTTP response. But I don't know how to handle these responses in Angular and I can't find any example of how to do that. Maybe someone can help this out?
My controller:
[HttpPost]
[Authorize]
public async Task<IActionResult> GenerateReport([FromBody] ReportDataFilteredModel data)
{
if (data.Report == null)
throw new ApiException("Report was not selected!");
var filePath = await _reportService.GenerateReport(data);
var response = HttpContext.Features.Get<IHttpResponseBodyFeature>();
response.DisableBuffering();
await response.StartAsync();
int i = 0;
var chunks = _fileService.ReadChunks(filePath, 1048576).ToList();
foreach (var block in chunks)
{
i++;
await response.Writer.WriteAsync(new ReadOnlyMemory<byte>(block));
await response.Writer.FlushAsync();
await Task.Delay(100);
Log.Information($"{i} chunk was send. Left {chunks.Count - i} more");
}
await response.CompleteAsync();
Log.Information("Excel was send succesfuly.");
return Ok();
}
My service.ts:
this.fileService.generateReport(this.reportDataFilteredModel).subscribe(
(response: Blob) => {
console.log(response);
const blob: any = new Blob([response], { type: 'application/vnd.ms-excel' });
const url = window.URL.createObjectURL(blob);
fileSaver.saveAs(blob, excelFileName);
this.notifier.showSuccessNotification();
},
(_error: HttpErrorResponse) => {
this.isExportingData = false;
this.notifier.showCustomNotification('error', _error.error);
console.log(_error);
},
() => {
this.isExportingData = false;
});
My API call method:
generateReport(appliedReportFilters: ReportDataFilteredModel): any{
const options = {
headers: new HttpHeaders({
Authorization: 'Bearer' + ' ' + localStorage.getItem('token'),
Connection: 'keep-alive'
}),
responseType: 'arraybuffer' as 'arraybuffer',
reportProgress: true
};
return this.http.post(`${environment.apiUrl}/api/reports`, appliedReportFilters, options)
.pipe(
catchError(err => {
if (err instanceof HttpErrorResponse || err.error instanceof ArrayBuffer || err.error.type === 'application/json') {
return new Promise<any>((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e: Event) => {
try {
const errmsg = JSON.parse((e.target as any).result);
reject(new HttpErrorResponse({
error: errmsg.Message,
headers: err.headers,
status: err.status,
statusText: err.statusText,
url: err.url ?? ''
}));
} catch (e) {
reject(err);
}
};
reader.onerror = (e) => {
reject(err);
};
reader.readAsText(err.error);
});
}
return throwError(err);
}));}
I tried without pipe and how I understand it doesn't have any difference.
I take examples of Chunks from there: link

Passing potential huge files in chunks to Web API

I have to pass potential huge files from an ASP.NET Core middle Server to an ASP.NET backend.
I can’t use the ASP.NET backend web API directly, I have to go over a MVC Controller.
Currently my middle Server gets the file in Chunks (and does some verification), saves it to disk and after it completes it rereads it in chunks to pass it forward.
Is there an easy way to pass the chunks without buffering the file?
I currently got this:
MVC Controler:
[HttpPost]
public ActionResult UploadChunk(IFormFile fileChunk, string chunkMetadata)
{
if (!string.IsNullOrEmpty(chunkMetadata))
{
var metaDataObject = JsonConvert.DeserializeObject<ChunkMetadata>(chunkMetadata);
...
AppendContentToFile(tempFilePath, fileChunk); //writes file with FileMode.Append,
}
}
my upload to back end [Edit]:
public IHttpActionResult FileUpload(string fileUri)
{
try
{
if (Request.Content.IsMimeMultipartContent())
{
var configProvider = Resolve<IApplicationConfigurationProvider>();
var uploadRootDir = configProvider.TemporaryFileUploadPath;
var streamProvider = new MultipartStreamProvider(uploadRootDir);
// If the file is huge and is not split into chunks, the 'ReadAsMultipartAsync' call
// takes as long as full file has been copied
var readResult = Request.Content.ReadAsMultipartAsync(streamProvider).Result;
var fileSvc = Resolve<IFileService>();
string targetFilePath = string.Empty;
foreach (MultipartFileData fileData in streamProvider.FileData)
{
ContentDispositionHeaderValue contentDisposition = fileData.Headers.ContentDisposition;
string fileName = contentDisposition.FileName;
if (!GetFileName(fileName, out var targetFileName))
{
return BadRequest($"ContentDisposition.FileName must match 'file' of URI-query! Actual: {targetFileName}");
}
var rawSourceFileInfo = new FileInfo(targetFileName);
if (contentDisposition.Size.HasValue && contentDisposition.Size.Value > 0)
{
if (!fileSvc.CreateNewFilePath(fileUri, new PathOptions(true), out var validFileFullPath))
{
return BadRequest($"Unable to create physical-path from fileId='{fileUri}'");
}
targetFilePath = validFileFullPath.FullName;
fileSvc.AddChunk(validFileFullPath.FullName, contentDisposition.Size.Value, fileData.LocalFileName);
}
else
{
return BadRequest("File upload must set a valid file-length in ContentDisposition");
}
}
return Ok(targetFilePath);
}
else
{
return BadRequest("File upload must be a 'IsMimeMultipartContent'");
}
}
catch (Exception error)
{
LogError(error, "FileUpload");
return InternalServerError(error);
}
}
Thanks in advance for any help!
[Edit]
my not working call from client to back end:
<script>
$(document).ready(function (e) {
$("#uploadImage").on('change', (function (e) {
// append file input to form data
var fileInput = document.getElementById('uploadImage');
var file = fileInput.files[0];
var formData = new FormData();
formData.append('uploadImage', file);
$.ajax({
url: "http://localhost/service/filestore/v1/upload?fileUri=someText",
type: "POST",
data: formData,
contentType: false,
cache: false,
processData: false,
success: function (data) {
if (data == 'invalid') {
// invalid file format.
$("#err").html("Invalid File !").fadeIn();
}
else {
// view uploaded file.
$("#preview").html(data).fadeIn();
$("#form")[0].reset();
}
},
error: function (e) {
$("#err").html(e).fadeIn();
}
});
}));
});
</script>

open file from ajax responce in mvc c#

I want to open file from Ajax response. here is the code. Here Ajax call response contain PDF file.
I want to open file in new tab of browser.here i am using mvc framework.
function ViewPDF(key){
$.ajax({
url: '#Url.Action("OpenDocument", "DocumentApproveUser")',
type: "POST",
data: { "key": key},
async: true,
cache: false,
success: function (data, status, xhr) {
alert(data);
window.open(data);
if (xhr.getResponseHeader("Forcefullylogin") == "true") {
var url = "/Login/Login";
window.location.href = url;
}
else {
}
},
error: function (error) {
$("#divLoading").hide();
if (error.getResponseHeader("Forcefullylogin") == true") {
var url = '#Url.Action("Login", "Login")';
window.location.href = url;
}
else {
alert('Something went wrong in system.Please try again later!or contact to system administrator.');
}
}
});
}
Server Code :
see below is code of my controller. this code return pdf file as ajax response.
I want to open that response in my browser.
[HttpPost]
public ActionResult OpenDocument(string key)
{
try
{
int Id = 0;
try
{
byte[] data = Convert.FromBase64String(key);
string decodedString = System.Text.Encoding.UTF8.GetString(data);
if (!(String.IsNullOrEmpty(decodedString)))
Id = Convert.ToInt32(decodedString);
}
catch (Exception ex)
{
ViewBag.ErrorName = "An error occured while opening document.";
base.ErrorLogger.Error("***OpenDocument***", ex);
return null;
}
DocumentApproveViewModel vm = new DocumentApproveViewModel();
vm.DocumentsApprovalModel = DocumentApproveViewModel.GetDocTransactionModelList(_repo.GetAll());
DocumentApprovalModel lst;
lst = (from x in vm.DocumentsApprovalModel where x.Id.Equals(Id) select x).FirstOrDefault();
base.Logger.InfoFormat("User : '{0}' going to access pdf document at {1} ", SessionFactory.CurrentUser.Name, System.DateTime.Now);
/////////////////////////////////////////////////////////////////////
ICollection<PasswordManagementViewModel> passwordList = null;
PasswordManagementViewModel password = null;
passwordList = PasswordManagementViewModel.GetSystemEncryptionKeyList(_encryption.GetAll());
password = passwordList.OrderByDescending(x => x.CreatedDateTime).FirstOrDefault();
string decryptPassword = Base64EncodeDecode.Decrypt(password.EncryptionKey, true);
/////////////////////////////////////////////////////////////////////
// Inhariting Logic from PDFSharpUtil Class.
byte[] PdfFileByte = _docSecurity.OpenPdfFile(lst.File, decryptPassword, SessionFactory.CurrentUser.EncryptionKey, SessionFactory.CurrentUser.Name, lst.DocumentTransactionName, false, SessionFactory.PdfViewCount);
/// Added logic for adding data into Document History ///
DocumentHistory objDocumentHistory = new DocumentHistory();
objDocumentHistory.SentTo = null;
objDocumentHistory.Timestamp = DateTime.UtcNow;
objDocumentHistory.ActionPerformedBy = SessionFactory.CurrentUser.Id;
objDocumentHistory.Action = EDocumentAction.View;
objDocumentHistory.DocumentId = Id;
_docHistoryRepo.Add(objDocumentHistory);
//Increment view count not to ask password from second attempt to open PDF file
SessionFactory.PdfViewCount++;
return File(PdfFileByte, "application/pdf");
}
catch (Exception ex)
{
ViewBag.ErrorName = "An error occured while opening Document";
base.ErrorLogger.Error("***OpenDocument :: DocumentView***", ex);
}
return null;
}
Do not try to use ajax to download file. Just open the url in a new browser tab. Based on your browser settings, it will either open in the tab or ask whether you want to save it.
You can set the new url to window.location.href
function ViewPDF(key)
{
var url= '#Url.Action("OpenDocument", "DocumentApproveUser")?key='+key,
window.location.href = url;
}
Based on the browser setting, the above 2 approaches will either ask user whether he wishes to download or open the file or simply download/open the file. If you prefer to show the file content directly in the browser, you may send a filestream to the browser.
Here is a quick example, which reads the pdf from a disk in the Contents/Downloads directory in app root and return the file stream.
public ActionResult View(int id)
{
var pathToTheFile=Server.MapPath("~/Content/Downloads/sampleFile.pdf");
var fileStream = new FileStream(pathToTheFile,
FileMode.Open,
FileAccess.Read
);
return new FileStreamResult(fileStream, "application/pdf");
}

IE11 saves file from Web API without extension

I have Angular SPA which downloads a file from ASP.NET Web API 2 using the following approach:
Angular Code
$scope.downloadFile = function(httpPath) {
// Use an arraybuffer
$http.get(httpPath, { responseType: 'arraybuffer' })
.success( function(data, status, headers) {
var octetStreamMime = 'application/octet-stream';
var success = false;
// Get the headers
headers = headers();
// Get the filename from the x-filename header or default to "download.bin"
var filename = headers['x-filename'] || 'download.bin';
// Determine the content type from the header or default to "application/octet-stream"
var contentType = headers['content-type'] || octetStreamMime;
try
{
// Try using msSaveBlob if supported
console.log("Trying saveBlob method ...");
var blob = new Blob([data], { type: contentType });
if(navigator.msSaveBlob)
navigator.msSaveBlob(blob, filename);
else {
// Try using other saveBlob implementations, if available
var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
if(saveBlob === undefined) throw "Not supported";
saveBlob(blob, filename);
}
console.log("saveBlob succeeded");
success = true;
}
catch(ex)
{
console.log("saveBlob method failed with the following exception:");
console.log(ex);
}
if(!success)
{
// Get the blob url creator
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if(urlCreator)
{
// Try to use a download link
var link = document.createElement('a');
if('download' in link)
{
// Try to simulate a click
try
{
// Prepare a blob URL
console.log("Trying download link method with simulated click ...");
var blob = new Blob([data], { type: contentType });
var url = urlCreator.createObjectURL(blob);
link.setAttribute('href', url);
// Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
link.setAttribute("download", filename);
// Simulate clicking the download link
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
link.dispatchEvent(event);
console.log("Download link method with simulated click succeeded");
success = true;
}
catch(ex) {
console.log("Download link method with simulated click failed with the following exception:");
console.log(ex);
}
}
if(!success)
{
// Fallback to window.location method
try
{
// Prepare a blob URL
// Use application/octet-stream when using window.location to force download
console.log("Trying download link method with window.location ...");
var blob = new Blob([data], { type: octetStreamMime });
var url = urlCreator.createObjectURL(blob);
window.location = url;
console.log("Download link method with window.location succeeded");
success = true;
} catch(ex) {
console.log("Download link method with window.location failed with the following exception:");
console.log(ex);
}
}
}
}
if(!success)
{
//Fallback to window.open method
console.log("No methods worked for saving the arraybuffer, using last resort window.open");
window.open(httpPath, '_blank', '');
}
})
.error(function(data, status) {
console.log("Request failed with status: " + status);
// Optionally write the error out to scope
$scope.errorDetails = "Request failed with status: " + status;
});
};
httpPath is a url of my Web API Controller. The controller has the following code:
Web API Code
[HttpGet]
[Authorize]
public async Task<HttpResponseMessage> DownloadItems(string path)
{
try
{
Stream stream = await documentsRepo.getStream(path);
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "document.zip",
};
result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentLength = stream.Length;
return result;
}
catch (Exception e)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, e);
}
}
The above setup works perfectly well in all browsers (a .zip file is downloaded), except IE11.
For unknown reason, IE11 downloads the file without .zip extension
Any idea what could be wrong with my API Controller?

Categories