I'm returning a FileContentResult from my c# .NET API, in my angular 7 application I receive it as a blob with HttpClient and then use the following to open a new tab and try to display it.
this.myService.GetPdfBlob().subscribe(data => {
var fileURL = window.URL.createObjectURL(data);
window.open(fileURL, '_blank');
})
After doing this it opens up a new tab, but I see a huge json object's text where it starts with FileContents property and that value is a massive string that I imagine is the bytes of the pdf document. It looks kind of like this:
{"FileContents":"JVBERi0xLjUNJeLjz9MNCjI1IDAgb2JqDTw8L0xpbmVhcml6ZWQgMS9MIDg1NzU1L08gMjcvRSA3MzgzMy9OIDQvVCA4NTQxNS9IIFsgNDc5IDIyMl0+Pg1lbmRvYmoNICAgICAgICAgICAgICAgICAgDQozOCAwIG9iag08PC9EZWNvZGVQYXJtczw8L0NvbHVtb...it keeps going..", "FileDownloadName":"MyBigFile.pdf"}
How do I make it display the actual pdf document and not this big JSON object of my FileContentResult? I eventually need to display multiple file types, but I'm just starting with pdf.
I changed up my C# code slightly to return an object that has a bytes array byte[] called Bytes and a filename. I still had the problem until I did this in my angular code:
this.http.get(myUrl).subscribe(response => {
var byteCharacters = atob(response.Bytes);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = new Blob([byteArray], {type: "application/pdf"});
var fileURL = URL.createObjectURL(blob);
window.open(fileURL, '_blank');
})
Related
I have a script that uploads a video to an API I built, and after it processes on the API side, a text file is returned to the client. The strange thing is, this only works with one type of file, a .QT file extension. Any other video type I try to send sends and empty video. I have tried .mov, .mp4, and .qt and only the .qt uploads properly. I'll post my code below. Would anyone know what cause only the one file type to work? Nothing on the API side singles out the qt file. I believe this is an issue with this script.
public async void Function() {
Debug.Log("works1");
string filePath = "IMG_0491.mov";
//string filePath = ProcessMode.theFilePath;
var client = new HttpClient();
using (var multipartFormContent = new MultipartFormDataContent()) {
//Add the file
Debug.Log("works2");
var fileStreamContent = new StreamContent(File.OpenRead(filePath));
Debug.Log("works3");
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("video/mov");
multipartFormContent.Add(fileStreamContent, name: "file", fileName: filePath); //Originally Actual "Name`
//Send it
var response = await client.PostAsync("http://127.0.0.1:5000/", multipartFormContent); //Enter IP and Port of API when set up
Debug.Log("works4");
//Ensure it was successful.
response.EnsureSuccessStatusCode();
//Grab the animation data from the content.
var animation_data = await response.Content.ReadAsStringAsync();
Debug.Log(animation_data);
//Save to file.
//File.WriteAllTextAsync("AnimationFile.txt", animation_data);
await File.WriteAllTextAsync("AnimationFile.txt", animation_data);
Debug.Log("works5");
}
I've been having some problems when I try to convert a byte[] coming from C# into a pdf file using Angular.
When I perform the conversion the file is created but is corrupted and it can't be opened "We can't open this file", the file content is just basic text "Hello world".
Please let me know if you have any answer to this problem, the code I am using is below and I also try using "import { saveAs } from 'file-saver';" but it didn't work.
// THE WEB API CONTROLLER THAT GETS THE PDF BYTE ARRAY
[HttpPost("CreatePDF")]
public async Task<HttpResponseMessage> CreatePDF([FromBody] PDFContent pdfContent)
{
byte[] pdf = await _pdfGenerator.GeneratePDF(pdfContent.HtmlContent);
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(pdf);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
// UI SERVICE LAYER THAT CALLS THE SERVER SIDE
createPdfReport(pdfContent: PDFContent): Observable<any> {
return this.restApi.create("CreatePDF",pdfContent).pipe(
tap(pdf =>
console.log('pdf = ' + pdf)
));
}
// THE COMPONENT THAT CALLS THE SERVICE LAYER AND WAITS FOR THE RESPONSE
this.historyService.createPdfReport(pdfContent).subscribe(data => {
var file = new Blob(data, { type: 'application/octet-stream' })
var fileURL = URL.createObjectURL(data);
const link = document.createElement('a');
link.setAttribute('target', '_blank');
link.setAttribute('href', fileURL);
link.setAttribute('download', `file.pdf`);
document.body.appendChild(link);
link.click();
link.remove();
});
}
This is the response I get in Angular after the controller returns the byte[]:
I'm trying to download a PDF file from blazor sent by my WebAPI as byte[] (byteArray).
I know that my PDF is working because when I send it as filestreamResult and retrieve it directly from my WebAPI (using swagger for example), I get the correct PDF file.
My WebAPI:
var response = await GetResponse(new Query(request.AnalysisDate), ct);
string html = await _templateService.PerformanceReport(response);
MemoryStream stream = _pdfService.GeneratePdf(html);
return await stream.ToArray(); // as byte[]
Blazor Side:
var bytes = await _http.GetByteArrayAsync(endpoint);
await JsRuntime.InvokeVoidAsync("BlazorDownloadFile", "file.pdf", "application/pdf", bytes);
JS:
function BlazorDownloadFile(filename, contentType, content) {
// Create the URL
const file = new File([content], filename, { type: contentType });
const exportUrl = URL.createObjectURL(file);
// Create the <a> element and click on it
const a = document.createElement("a");
document.body.appendChild(a);
a.href = exportUrl;
a.download = filename;
a.target = "_self";
a.click();
// We don't need to keep the object url, let's release the memory
// On Safari it seems you need to comment this line... (please let me know if you know why)
URL.revokeObjectURL(exportUrl);
}
I Correctly download a PDF file named file.pdf but it looks corrupted.
Is this the correct approach with .NET 6?
Should I send a FileStreamResult from the Web API (I Like the fact that I can get the file directly from swagger)?
I can't really find a good example for blazor on how to do this. Any help would be appreciated.
I am using another aproach. I send base64String. I convert byte array to base64 string then i pass the string to javascript function
Server side call:
js.InvokeVoidAsync("jsSaveAsFile",
filename,
Convert.ToBase64String(GetFileByteArrayFunction())
);
javascript function:
function jsSaveAsFile(filename, byteBase64) {
var link = document.createElement('a');
link.download = filename;
link.href = "data:application/octet-stream;base64," + byteBase64;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);}
js -> using Microsoft.JSInterop.
You should send your file like this:
app.MapGet("/download", () =>
{
//...
Results.File("myfile.pdf");
});
there are some overloads of this method you can pick up the one that fit your requirements.
I am trying to download a simple xlsx file using web api but the file is always corrupt and I am yet to figure out why.I am using C# ClosedXML.Excel and following a basic example which can be found here:
ClosedXml examples
[HttpGet]
[Route("campaigns/{id}/contact-points/excel")]
[SwaggerResponse(491, "TokenInvalid")]
[SwaggerResponse(HttpStatusCode.NotFound)]
[SwaggerResponse(HttpStatusCode.Forbidden)]
[ResponseType(typeof(HttpResponseMessage))]
public async Task<IHttpActionResult> GetCampaignContactPointsExcel(int id, int frequency, string txtFrequency)
{
var wb = new XLWorkbook();
var ws1 = wb.Worksheets.Add("Sheet1");
ws1.Cell("A1").SetValue(1).AddToNamed("value1");
var ws2 = wb.Worksheets.Add("Sheet2");
ws2.Cell("A1").SetFormulaA1("=value1").AddToNamed("value2");
var responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
using (var memoryStream = new MemoryStream())
{
wb.SaveAs(memoryStream);
responseMessage.Content = new ByteArrayContent(memoryStream.ToArray());
responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
responseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "ContactPoints.xlsx"
};
memoryStream.Close();
}
return ResponseMessage(responseMessage);
}
I am also using swagger and when I click on the link it downloads the file and opens it as a xlsx file but it always says it's corrupt.
There is nothing wrong in the backend code. On the frontend side you need to set responseType='blob'
Seems like this is an issue related to swagger ui 2. Please refer to [https://github.com/swagger-api/swagger-ui/issues/1605][1].
You can try curl or Postman to see if it downloads the file correctly.
While calling the service set Response type as 'blob'
return this.http.get(this.url +'PA/Downloadexcel/'+revisionId,{ responseType :'blob'});
I'm trying to download a blob from private Azure Blob storage container and display it in an image tag.
On the question below you can see how I'm returning the blob from the Web API in Stream format.
Getting 403 error when trying to retrieve an Azure blob on Web API request
From the HTTP response, I am able to retrieve the content type of the blob by including it on the headers section of the request. To use it to generate the data URI to be used on the image tag. I understand I need to convert the Stream into a base64 string to be able to include it on the src attribute of an image tag. I'm currently struggling to convert the result from the HTTP request into a base64 string.
I have created this js fiddle which contains the data (image) received from the HTTP request along with my attempt to convert the data into a base64 string:
'http://jsfiddle.net/chesco9/6a7ohgho/'
EDIT
Thank you Tom for your help. I was able to implement your solution and it worked out. I had been stuck on this problem for a few days now.
public async Task<AzureBlobModel> DownloadBlob(Guid blobId)
{
try
{
//get picture record
Picture file = await _media.GetPictureAsync(blobId);
// get string format blob name
var blobName = file.PictureId.ToString() + file.Extension;
if (!String.IsNullOrEmpty(blobName))
{
var blob = _container.GetBlockBlobReference(blobName);
// Strip off any folder structure so the file name is just the file name
var lastPos = blob.Name.LastIndexOf('/');
var fileName = blob.Name.Substring(lastPos + 1, blob.Name.Length - lastPos - 1);
var fileLength = blob.Properties.Length;
var stream = await blob.OpenReadAsync();
MemoryStream ms = new MemoryStream();
stream.CopyTo(ms);
var result = new AzureBlobModel()
{
FileName = fileName,
FileSize = blob.Properties.Length,
Stream = stream,
ContentType = blob.Properties.ContentType,
StreamBase64 = Convert.ToBase64String(ms.ToArray())
};
return result;
}
}
catch(Exception ex)
{
await _log.CreateLogEntryAsync("exception thrown: " + ex.ToString());
}
await _log.CreateLogEntryAsync("returning null");
// Otherwise
return null;
}
I'm currently struggling to convert the result from the HTTP request into a base64 string.
Base on my understanding, now you can download the blob from the Azure storage.
According to your mentioned link, the WebApi return the AzureBlobModel.
We can convert the stream to base64 string easily with C# code backend.You can add following code in your code. If it is prossible, return this value in the AzureBlobModel.
MemoryStream ms = new MemoryStream();
stream.CopyTo(ms);
string strBase64 = Convert.ToBase64String(ms.ToArray());