Issue
I know this question has been asked multiple times, but I cannot get a macro enabled file retrieved from Azure Storage Container via a Web Api to download in the front end (angular)
This is my API method, goes to Storage account, grabs the file and downloads:
[HttpGet("download/{fileSource}")]
public async Task<FileResult> DownloadFile([FromRoute] string fileSource)
{
fileSource = "MyDetails.xlsm";
var storageAccount = CloudStorageAccount.Parse(_storageConnectionString);
var blobCLient = storageAccount.CreateCloudBlobClient();
var container = blobCLient.GetContainerReference("documents");
var blockBlob = container.GetBlockBlobReference(fileSource);
var memoryStream = new MemoryStream();
try
{
await blockBlob.DownloadToStreamAsync(memoryStream);
memoryStream.Position = 0;
return File(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileSource);
}
catch (Exception ex)
{
throw;
}
finally
{
memoryStream.Dispose();
}
}
This is my frontend call:
downloadFile(fileSource: string): Observable<Blob> {
let url = `${environment.APIURL}docs/download/${fileSource}`;
return this.http.get<Blob>(url, this.getDownloadFileHeaderOptions)
.pipe(
tap(response => {
const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileSource;
a.click();
})
);
}
fileSource = name of file
The getDownloadFileHeaderOptions
is:
getDownloadFileHeaderOptions = {
headers: new HttpHeaders(
{
responseType: 'blob'
})
};
The error I get is "Cannot access a closed Stream.", this is thrown after the API has returned File(memoryStream...
Solution
You are facing this issue because you are disposing of the MemoryStream
in a finally
block. This means that as soon as your DownloadFile
method completes and returns the FileResult
, the MemoryStream
is disposed of. However, the FileResult
returned by the method doesn't actually read from the MemoryStream
until after the method has returned, at which point the stream is already disposed.
To resolve the issue you can avoid disposing the MemoryStream
within your API method.
You can try this below code:
[HttpGet("download/{fileSource}")]
public async Task<FileResult> DownloadFile([FromRoute] string fileSource)
{
fileSource = "ServicePointDetails.xlsm";
var storageAccount = CloudStorageAccount.Parse(_storageConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("documents");
var blockBlob = container.GetBlockBlobReference(fileSource);
var memoryStream = new MemoryStream();
try
{
await blockBlob.DownloadToStreamAsync(memoryStream);
memoryStream.Position = 0;
return File(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileSource);
}
catch (Exception ex)
{
throw;
}
}
"SyntaxError: Unexpected Token 'P' is not a valid JSON at JSON.parse" menas frontend is trying to parse the received response as JSON, but the content of the response is not in JSON format. In your code you are trying to download a file, the content of the response should be a stream of files, not JSON.
Front-end code:
downloadFile(fileSource: string): Observable {
let url = `${environment. APIURL}docs/download/${fileSource}`;
return this.http.get(url, { responseType: 'blob' }) // Make sure the response type is 'blob'
.pipe(
tap(response => {
const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window. URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileSource;
document.body.appendChild(a); Make sure that the A element is added to the document
a.click();
document.body.removeChild(a); clean
})
);
}
Answered By - Jalpa Panchal
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.