This is my Controller code for returning the XDocument file. I turn it into a MemoryStream then return it as base 64 string.
[HttpPost]
public ActionResult ImportAcesFileAjax(TransactionViewModel transactionViewModel)
{
XDocument xDocument = new XDocument();
try
{
if (transactionViewModel.File.IsNotNullObject())
{
ImportService importService = new ImportService();
xDocument = importService.ProcessExcelFileForTransformation(transactionViewModel);
_logger.Information($"Finished processing {transactionViewModel.File.FileName}");
}
}
catch (Exception e)
{
_logger.Error($"Error: {e.Message}");
_logger.Error(e.StackTrace);
throw;
}
return ReturnImportXmlAsByte(xDocument, "import.xml");
}
protected ActionResult ReturnImportXmlAsByte(XDocument xDocument, string xmlFilename)
{
using (MemoryStream stream = new MemoryStream())
using(XmlWriter xmlWriter = XmlWriter.Create(stream))
{
xDocument.WriteTo(xmlWriter);
var byteLength = stream.ToArray().Length;
var base64String = Convert.ToBase64String(stream.ToArray(), 0, byteLength);
return Json(new {base64String = base64String, xmlFilename = xmlFilename});
}
}
I use the following code in the front end:
$.ajax({
url: requestUrl,
type: "POST",
data: formData,
contentType: false,
processData: false,
success: function (excelByteResult) {
var bytes = base64ToBytes(excelByteResult.base64String);
var blob = new Blob([bytes], { type: "text/xml" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = excelByteResult.xmlFilename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("Xhr: " + jqXHR.responseText);
console.log("Status: " + status);
console.log("Error Thrown: " + errorThrown);
}
});
The ajax call is successful but I can't seem to access the file. Are there any alternatives to my approach we may work? The requirement is to download an xml file via ajax
Related
I am trying to download image using below code. But file showed as download with error
(Failed - Network error)
I am using below code
$scope.downloadDocument = function (doc) {
$http({
method: "GET",
url: appConfig.apiUrl + "/downloadDoc/" + $scope.array.nic + "/" + doc.fileName
}).then(function (response) {
console.log(response);
$scope.downloadedImage = response.data;
var a = document.createElement("a"); //Create <a>
a.href = "data:image/jpge;base64," + response.data; //Image Base64 Goes here
console.log(a);
a.download = doc.fileName; //File name Here
a.click(); //Downloaded file
}, function (response) {
$log.log(response);
});
}
My response
When I click download button. It shows as download with error. Check below image
How i do this correctly. please help
You need to add responseType:'blob' your request,
also I prefer use object url (https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) for downloading files
function downloadBlob(blob, name) {
const data = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = data;
link.target = '_self';
link.download = name;
link.dispatchEvent(
new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
})
);
setTimeout(() => {
window.URL.revokeObjectURL(data);
link.remove();
}, 100);
}
$scope.downloadDocument = function (doc) {
$http({
method: "GET",
url: appConfig.apiUrl + "/downloadDoc/" + $scope.array.nic + "/" + doc.fileName,
responseType: 'blob'
}).then(function (response) {
console.log(response);
downloadBlob(new Blob([response.data]), doc.fileName);
}, function (response) {
$log.log(response);
});
}
I want to download an excel from my angular controller using blob.
the server code of the mvc application is
[HttpGet]
public HttpResponseMessage Download(SearchModel search){
string fileName = "test.xls"
byte[] file = GetFile(search)
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = new ByteArrayContent(file.ToArray());
httpResponseMessage.Content.Headers.Add("x-filename", fileName);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName;
return httpResponseMessage;
}
and the js
$http({ method: 'GET', url: 'Download', params: {...some params}, responseType: 'arrayBuffer' })
.success(function (data, status, headers) {
headers = headers();
var filename = headers['x-filename'];
var contentType = headers['content-type'];
var linkElement = document.createElement('a');
try {
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(new Blob([data],{type: "application/octet-stream"}));
return;
}
var a = $("<a style='display: none;'/>");
var url = window.URL.createObjectURL(new Blob([data], {type: "application/octet-stream"}));
a.attr("href", url);
a.attr("download", "name");
$("body").append(a);
a[0].click();
window.URL.revokeObjectURL(url);
a.remove();
}
catch (ex) {
console.log(ex);
}
})
.error(function () { });
but there are some problems:
the filename in the javascript side is undefined and not test.xls as in the server side
the contentType is "text/html; charset=utf-8" and not "application/octet-stream" as in the server side
the file that is downloaded is this
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.ByteArrayContent, Headers:
{
x-filename: test.pdf
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=test.pdf
}
The file is a JSON with header information .
Why is this file wrong?
When I try to upload, I get the following error:
Error code:1363030,
msg: Your video upload timed out before it could be completed. This is probably because of a slow network connection or because the video you're trying to upload is too large. Please try again.
I'm using Facebook Javascript SDK 2.5.
What am I missing or wrong?
<script>
var files;
var fileData = '';
function handleFileSelect(evt) {
files = evt.target.files; // FileList object
var input = evt.target;
var reader = new FileReader();
reader.onload = function (e) {
fileData = e.target.result;
};
reader.readAsDataURL(input.files[0]);
// files is a FileList of File objects. List some properties.
var output = [];
for (var i = 0, f; f = files[i]; i++) {
output.push('<li class="list-group-item">', escape(f.name), '(', f.type || 'n/a', ') - ',
f.size, ' bytes','</li>');
}
document.getElementById('list').innerHTML = output.join('');
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
$(document).ready(function()
{
$("#upload").click(function(){
var token = $('#token').val();
FB.api(
"/me/videos",
"POST",
{
"access_token" : token,
"title" : 'test',
"source": fileData
},
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);
})
});
</script>
here is the sample site
Just hit this in Node.js. This Facebook error occurs if you don't specify the content type and file name of the attached file (i.e. if you pass it as an inline field value, not as an attached file).
Not sure how to do it via FB.api, but with request-promise module (and ES7 async functions via Babel) it looks like this:
import request from 'request-promise'
async function uploadVideoToFacebook (buf) {
let url = 'https://graph-video.facebook.com/v2.5/' + pageId + '/videos?access_token=' + pageToken
let formData = {
title: 'Video title',
description: 'Timeline message...',
source: {
value: buf,
options: {
filename: 'video.mp4',
contentType: 'video/mp4'
}
}
}
return await request({ method: 'POST', url, formData })
}
and with XMLHttpRequest on the client-side you would do something like:
var blob = new Blob(videoDataHere, { type: 'video/mp4' })
var formData = new FormData();
formData.append('source', blob);
formData.append('message', 'Spartan Overlay');
var ajax = new XMLHttpRequest()
ajax.onreadystatechange = ...
ajax.open('POST', 'https://graph.facebook.com/' + userId + '/videos?access_token=' + accessToken, true)
ajax.send(formData)
If I post a PDF to my vendors API, they return me a .png file as a blob (see update 2 as I am now unsure if they are returning blob data).
I would like to push this into Azure Blob Storage. Using my code listed below, it pushes something in, but the file is corrupted. Example: downloading the .png from Azure Blob Storage and trying to open it with Paint gives the following error:
This is not a valid bitmap file, or its format is not currently
supported.
I have verified that the image is sent to me correctly as the vendor is able to open the .png on their side. I am wondering if I need to convert this to base64 or save it to a local Web directory before uploading it to Azure Blob Storage.
Here is my Angular front end Controller that calls my Node/Express backend for uploading to Azure once it receives the returned "image":
$.ajax({
url: 'http://myvendorsapi.net/uploadPDF,
type: "POST",
data: formdata,
mimeType: "multipart/form-data",
processData: false,
contentType: false,
crossDomain: true,
success: function (result) {
var containerName = 'container1';
var filename = 'Texture_0.png';
var file = result;
$http.post('/postAdvanced', { containerName: containerName, filename: filename, file: file }).success(function (data) {
console.log("success!");
}, function (err) {
//console.log(err);
});
},
error: function (error) {
console.log("Something went wrong!");
}
})
}
Here is my Node/Express backend that uploads the blob to Azure Blob Storage. It gives no error, but the file can't be opened/gives the error stated above when opened in Paint:
app.post('/postAdvanced', function (req, res, next) {
var containerName = req.body.containerName;
var filename = req.body.filename;
var file = req.body.file;
blobSvc.createBlockBlobFromText(containerName, filename, file, function (error, result, response) {
if (!error) {
res.send(result);
}
else {
console.log(error);
}
});
})
Update 1: The answer provided here allows me to pass in the URL of the vendors API for some endpoints: Download file via Webservice and Push it to Azure Blob Storage via Node/Express
It works as it writes the file at the endpoint to a temp folder. In my current scenario, I upload a PDF file and it returns an image file that I need to upload to Azure Blob Storage. Is there a way to use the answer here, but adjust it for a file that I already have (since it is returned to me) versus file streaming from a URL?
Update 2: In console logging the returned "file", it looks like it may be data. I am not sure, it looks like this:
Is this actually data, and if so, how do I make this into a file for upload?
UPDATE 3:
Since it appears that jQuery AJAX can't manage binary returns. I am able to "open" the blob using XMLHTTPResponse as follows, but I can't seem to push this into Azure as it gives me the following error:
TypeError: must start with number, buffer, array or string
Here is my request. Note that the file opens properly:
var form = document.forms.namedItem("fileinfo");
form.addEventListener('submit', function (ev) {
var oData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("POST", "http://myvendorsapi/Upload", true);
xhr.onload = function (oEvent) {
if (xhr.status == 200) {
var blob = new Blob([xhr.response], { type: "image/png" });
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
console.log(blob);
var containerName = boxContainerName;
var filename = 'Texture_0.png';
$http.post('/postAdvanced', { containerName: containerName, filename: filename, file: blob }).success(function (data) {
//console.log(data);
console.log("success!");
}, function (err) {
//console.log(err);
});
} else {
oOutput.innerHTML = "Error " + xhr.status + " occurred when trying to upload your file.<br \/>";
}
};
xhr.send(oData);
ev.preventDefault();
}, false);
createBlockBlobFromText will work with either string or buffer. You might need a buffer to hold the binary content due to a known issue of jQuery.
For a workaround, there are several options:
Option 1: Reading binary filesusing jquery ajax
Option 2: Use native XMLHttpRequest
Option 3: Write frontend with Node as well and browserify it.
Your frontend code may look like:
var request = require('request');
request.post('http://myvendorsapi.net/uploadPDF', function (error, response, body) {
if (!error && response.statusCode == 200) {
var formData = {
containerName: 'container1',
filename: 'Texture_0.png',
file: body
};
request.post({ uri: '/postAdvanced', formData: formData }, function optionalCallback(err, httpResponse, body) {
if (err) {
return console.error('upload failed:', err);
}
console.log('Upload successful! Server responded with:', body);
});
} else {
console.log('Get snapshot failed!');
}
});
Then the backend code may look like:
app.post('/postAdvanced', function (req, res, next) {
var containerName = req.body.containerName;
var filename = req.body.filename;
var file = req.body.file;
if (!Buffer.isBuffer(file)) {
// Convert 'file' to a binary buffer
}
var options = { contentType: 'image/png' };
blobSvc.createBlockBlobFromText(containerName, filename, file, options, function (error, result, response) {
if (!error) {
res.send(result);
} else {
console.log(error);
}
});
})
Below I have the code to upload the image as binary in angular using FormData.
The server code will be the code to handle a regular file upload via a form.
var form = document.forms.namedItem("fileinfo");
form.addEventListener('submit', function (ev) {
var oData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("POST", "http://vendorapi.net/Upload", true);
xhr.onload = function (oEvent) {
if (xhr.status == 200) {
var blob = new Blob([xhr.response], { type: "image/png" });
//var objectUrl = URL.createObjectURL(blob);
//window.open(objectUrl);
//console.log(blob);
var formData = new FormData()
formData.append('file', blob);
formData.append('containerName', boxContainerName);
formData.append('filename', 'Texture_0.png');
$http.post('/postAdvancedTest', formData, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).success(function (data) {
//console.log(data);
console.log("success!");
// Clear previous 3D render
$('#webGL-container').empty();
// Generated new 3D render
$scope.generate3D();
}, function (err) {
//console.log(err);
});
} else {
oOutput.innerHTML = "Error " + xhr.status + " occurred when trying to upload your file.<br \/>";
}
};
xhr.send(oData);
ev.preventDefault();
}, false);
I have solved the issue (thanks to Yang's input as well). I needed to base64 encode the data on the client side before passing it to node to decode to a file. I needed to use XMLHTTPRequest to get binary data properly, as jQuery AJAX appears to have an issue with returning (see here: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/).
Here is my front end:
var form = document.forms.namedItem("fileinfo");
form.addEventListener('submit', function (ev) {
var oData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("POST", "http://vendorapi.net/Upload", true);
xhr.onload = function (oEvent) {
if (xhr.status == 200) {
var blob = new Blob([xhr.response], { type: "image/png" });
//var objectUrl = URL.createObjectURL(blob);
//window.open(objectUrl);
console.log(blob);
var blobToBase64 = function(blob, cb) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
var base64 = dataUrl.split(',')[1];
cb(base64);
};
reader.readAsDataURL(blob);
};
blobToBase64(blob, function(base64){ // encode
var update = {'blob': base64};
var containerName = boxContainerName;
var filename = 'Texture_0.png';
$http.post('/postAdvancedTest', { containerName: containerName, filename: filename, file: base64}).success(function (data) {
//console.log(data);
console.log("success!");
// Clear previous 3D render
$('#webGL-container').empty();
// Generated new 3D render
$scope.generate3D();
}, function (err) {
//console.log(err);
});
})
} else {
oOutput.innerHTML = "Error " + xhr.status + " occurred when trying to upload your file.<br \/>";
}
};
xhr.send(oData);
ev.preventDefault();
}, false);
Node Backend:
app.post('/postAdvancedTest', function (req, res) {
var containerName = req.body.containerName
var filename = req.body.filename;
var file = req.body.file;
var buf = new Buffer(file, 'base64'); // decode
var tmpBasePath = 'upload/'; //this folder is to save files download from vendor URL, and should be created in the root directory previously.
var tmpFolder = tmpBasePath + containerName + '/';
// Create unique temp directory to store files
mkdirp(tmpFolder, function (err) {
if (err) console.error(err)
else console.log('Directory Created')
});
// This is the location of download files, e.g. 'upload/Texture_0.png'
var tmpFileSavedLocation = tmpFolder + filename;
fs.writeFile(tmpFileSavedLocation, buf, function (err) {
if (err) {
console.log("err", err);
} else {
//return res.json({ 'status': 'success' });
blobSvc.createBlockBlobFromLocalFile(containerName, filename, tmpFileSavedLocation, function (error, result, response) {
if (!error) {
console.log("Uploaded" + result);
res.send(containerName);
}
else {
console.log(error);
}
});
}
})
})
So, I am trying to generate PDF file by passing the HTML string to the server to generate the byte streams using NReco library http://www.nrecosite.com/pdf_generator_net.aspx and return it back to the client side however, after I converted to blob format and save it with FileSaver library https://github.com/eligrey/FileSaver.js/, the saved file is unable to open.
Below are my code by far:
controller
string HtmlContent = model.HtmlContent;
string PageType = gradeReportPdfBindingModel.PageType;
string FileName = gradeReportPdfBindingModel.FileName;
var pdfDoc = new HtmlToPdfConverter();
if (string.IsNullOrEmpty(PageType))
pdfDoc.Orientation = PageOrientation.Default;
else
{
if (PageType == "Landscape")
pdfDoc.Orientation = PageOrientation.Landscape;
else
pdfDoc.Orientation = PageOrientation.Portrait;
}
var pdfBytes = pdfDoc.GeneratePdf(HtmlContent);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(pdfBytes);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = FileName;
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return result;
Customer.js
Customer.Export(exportModel).then(function (response) {
var file = new Blob([response.data], { type: 'application/pdf' });
saveAs(file, fileName);
});
HTTP POST call
Export: function (model) {
return $http({
method: 'POST',
headers: {
accept: 'application/pdf'
},
url: appSettings.serverPath + '/customer/export',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: $.param(model),
});
},
Response log from browser console:
Object { data: "%PDF-1.4 1 0 obj << /Title (��) /Cr…", status: 200, headers: ed/<(), config: Object, statusText: "OK" }
Edit 1:
I tried to return the result as HttpResponseMessage. How can I consume it on javascript side?
Edit 2:
I figured it out. This is how I do it.
Controller:
[HttpPost]
[Route("export")]
public IHttpActionResult GeneratePdf(model)
{
var pdfBytes = pdfDoc.GeneratePdf(HtmlContent);
var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(300), Priority = CacheItemPriority.NotRemovable };
var cacheId = Guid.NewGuid().ToString();
MemoryCache.Default.Add("pdfBytes_" + cacheId, pdfBytes, policy);
return Ok(cacheId);
}
[Authorize]
[HttpGet]
[Route("getPdf/{cacheId}/{fileName}")]
public HttpResponseMessage DownloadPdf(Guid CacheId, string FileName)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
try
{
var pdfBytes = (Byte[])MemoryCache.Default.Get("pdfBytes_" + CacheId);
MemoryCache.Default.Remove("pdfBytes_" + CacheId);
using (MemoryStream memorystream = new MemoryStream(pdfBytes))
{
response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new ByteArrayContent(memorystream.ToArray());
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = FileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
}
customer.js:
Customer.Export(model).then(function (response) {
Customer.DownloadPdf(response.data, fileName).then(function (response) {
var file = new Blob([response.data], { type: "application/pdf" });
saveAs(file, fileName);
}).catch(function (response) {
console.error('Error', response.status, response.data);
});
});
Http calls:
Export: function (model) {
return $http({
method: 'POST',
url: appSettings.serverPath + '/customer/export',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: $.param(model),
});
},
DownloadPdf: function (cacheId, fileName) {
return $http({
method: 'GET',
headers: {
accept: 'application/pdf'
},
responseType: 'arraybuffer',
url: appSettings.serverPath + '/customer/downloadPdf/' + cacheId + '/' + fileName,
headers: { 'Content-Type': 'application/pdf' }
});
}
I basically tested it on my local dev machine and it works, but when I published it Azure Web App, I was unable to get through the POST call and it returns status 500 after some time. Not sure why, but I am suspecting the MemoryCache I am using. Any idea will be appreaciated.
Thanks.