I'm experiencing an issue where a POST request I make fails with error code 500 and nothing is printed in my server side error log. It's almost as if the cloud method doesn't exist for it.
What is strange though, is that the same POST request performs fine with smaller files. It starts to fail when the file gets to around 1.4 MB. I've tried changing the limit parameter of express.bodyParser like so "app.use(express.bodyParser( { limit: 10000000 } ));", however this makes no difference.
Here's some code:
function uploadFile(pageState)
{
var reader = new FileReader();
reader.onload = function()
{
var data = reader.result;
var byteArrayObj = new Uint8Array(data);
var byteArray = new Array(byteArrayObj.length);
for(i=0; i<byteArray.length; i++) {
byteArray[i] = byteArrayObj[i];
}
var json = { data: byteArray };
var jsonString = JSON.stringify(json);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "/assetUploader/"+pageState.assetIdentifier+"/"+pageState.fileName+"/"+pageState.isAssetBundle+"/"+pageState.uploadToProduction, true);
xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.onload = function(evt) {
...
};
xmlhttp.send(jsonString);
};
reader.readAsArrayBuffer( pageState.fileRef );
}
On the cloud side:
app.post('/assetUploader/:fileIdentifier/:fileName/:isAssetBundle/:useProd', function(req, res)
{
console.log("assetUpload Post");
var fileId = req.params.fileIdentifier;
var fileName = req.params.fileName;
var assetBundle = req.params.isAssetBundle == "true";
var bytes = req.body.data;
var useProd = req.params.useProd == "true";
createParseFile(fileName, bytes, useProd).then( function(response) {
return currentVersionForAsset(fileId, useProd).then(function(versionNumber) {
return {
"fileIdentifier": fileId,
"file": {
"name": response.data.name,
"__type": "File"
},
"isAssetBundle": assetBundle,
"fileVersion": versionNumber+1
};
});
}).then(function(json) {
return createParseObject("DownloadableAsset", json, useProd);
}).then( function() {
res.send(200);
}, function(error) {
res.send(400, error.message);
});
});
Any help or suggestions would be much appreciated. Thanks for your time!
i suggest using res.setTimeout with a very large number of milisecs. perhaps your response simply times out, and from my experience node doesn't throw an error when it happens.
Related
I have one XMLHttpRequest in javascript which is posting the FormData object to server. This call works fine in Chrome. On Microsoft Edge, I am seeing this error: "SCRIPT0: SCRIPT0: 'FormData' is not defined".
Here is my snippet:
function saveRecording(recordingData, endpoint, token) {
var randomNumber = Math.floor(100000 + Math.random() * 900000);
randomNumber = randomNumber.toString().slice(0, 4);
var recordingName = "Recording " + randomNumber;
var sender = {
sliceNumber: 1,
sliceMaxSize: 1024 * 1024,
sliceStart: 0,
sliceEnd: 1024 * 1024,
total: recordingData.blob.size,
recordlength: recordingData.length.toFixed(2),
fileName: '',
sendSlice: function () {
if (this.total > 0) {
var slice = undefined;
if (this.total >= this.sliceMaxSize) {
this.sliceEnd = this.sliceStart + this.sliceMaxSize;
}
else {
this.sliceEnd = this.sliceStart + this.total;
}
slice = recordingData.blob.slice(this.sliceStart, this.sliceEnd);
var isLastSlice = !((this.total - this.sliceMaxSize) > 0);
console.log(slice);
var formData = new FormData();
formData.append("data", slice);
formData.append("sliceMaxSize", this.sliceMaxSize);
formData.append("sliceNumber", this.sliceNumber);
formData.append("fileName", this.fileName);
formData.append("dictation", JSON.stringify(recordingData.dictation));
formData.append("isLastSlice", isLastSlice);
formData.append("length", this.recordlength);
formData.append("encoding", recordingData.encoding);
formData.append("name", recordingName);
//var params = 'data=ipsum&sliceMaxSize=binny&sliceMaxSize=binny&sliceMaxSize=binny&sliceMaxSize=binny&sliceMaxSize=binny&sliceMaxSize=binny';
var sender_this = this;
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
//readyState=4 means request finished and response is ready
if (this.readyState == 4) {
if (this.status == 200) {
//Success
sender_this.fileName = JSON.parse(this.response).fileName;
sender_this.sliceNumber++;
sender_this.sliceStart = sender_this.sliceStart + sender_this.sliceMaxSize;
sender_this.total = sender_this.total - sender_this.sliceMaxSize;
// send the next slice
sender_this.sendSlice();
}
else {
//Fail
self.postMessage({ command: "saveRecordingFail", message: JSON.parse(this.response) });
}
}
};
request.open("POST", endpoint, true);
request.setRequestHeader("Accept", "application/json, text/plain, */*");
//request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
request.setRequestHeader(token.name, token.value);
request.send(formData);
}
else {
//recording completely sent to server, post message to worker caller
self.postMessage({ command: "saveRecordingSuccess" });
}
}
};
// send the slice
sender.sendSlice();
}
Workaround already tried:
I have already checked that FormData works in edge by manually trying on the edge console which created the FormData object successfully.
FormData working fine in console of edge browser
I tried to make a test with sample code below and find that it is working fine with MS Edge browser (user agent string: IE 11)
<!doctype html>
<script>
let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
// List key/value pairs
for(let [name, value] of formData) {
alert(`${name} = ${value}`);
}
</script>
</html>
I suggest you to check all the values that you are trying to append and verify that all are having proper and correct values.
I suggest you to check all the values one by one to find the problematic value.
It may help to narrow down the issue. Than it will be easier for you to fix the issue.
Reference:
FormData
I created a generator for PDF files that creates the file and sends to a specific path automatically after creation. Still I want to download it right after but didn't know how to do that. Any help would be appreciated. This is my generatorPdf.js :
module.exports={
pdfGenerator:function(data,pathfile){
var fonts = {
Roboto: {
normal: 'server/pdfgenerator/fonts/Roboto-Regular.ttf',
bold: 'server/pdfgenerator/fonts/Roboto-Medium.ttf',
italics: 'server/pdfgenerator/fonts/Roboto-Italic.ttf',
bolditalics: 'server/pdfgenerator/fonts/Roboto-MediumItalic.ttf'
}
};
var datePaiements='';
var dateFinPaiements='';
if(data.abonnement[0].datePaiement!=null)
datePaiements= new Date( data.abonnement[0].datePaiement.toString());
if(datePaiements!=='')
{
dateFinPaiements= ('0'+datePaiements.getDate()).slice(-2).toString()+'/'+('0'+(datePaiements.getMonth()+1)).slice(-2).toString()+'/'+(datePaiements.getFullYear()+1).toString();
datePaiements=('0'+datePaiements.getDate()).slice(-2).toString()+'/'+('0'+(datePaiements.getMonth()+1)).slice(-2).toString()+'/'+datePaiements.getFullYear().toString();
}
var dateFacture= new Date(data.abonnement[0].timestampCreation.toString());
dateFacture= ('0'+dateFacture.getDate()).slice(-2).toString()+'/'+('0'+(dateFacture.getMonth()+1)).slice(-2).toString()+'/'+dateFacture.getFullYear().toString();
var PdfPrinter = require('pdfmake/src/printer');
var printer = new PdfPrinter(fonts);
var fs = require('fs');
var dd = {
content: [ ..............],
footer:{.............}
}
try{
var pdfDoc = printer.createPdfKitDocument(dd);
if (fs.existsSync(pathfile)) {//server/pdfgenerator/documentpdf/basics21.pdf
fs.unlink(pathfile, (err) => {//server/pdfgenerator/documentpdf/basics21.pdf
if (err) {
console.error(err)
return
}
})
}
pdfDoc.pipe(fs.createWriteStream(pathfile)).on('finish',function(){//server/pdfgenerator/documentpdf/basics21.pdf
});
}
catch(e){
console.log(e);
return null;
}
}
}
and this is my remote method in Loopback to send the pdf to a path and where probably I have to do the download of the file:
cm_abonnements.getAbonnementById= async (options,req,res)=>{
const token = options && options.accessToken;
const userId = token && token.userId;
try{
if(userId!==null){
let dataComedien= await app.models.cm_comediens.getComedienByUser(userId);
let argAbn={};
const form = new formidable.IncomingForm();
var formPromise = await new Promise(function(resolve,reject){
form.parse(req,function(err,fields,files){
if(err)
{
reject(err);
return-1
}
console.log(fields.key)
argAbn.idAbonnement=fields.key;
resolve();
})
})
let dataFac=await cm_abonnements.find({where :{and :[{idAbonnement:argAbn.idAbonnement},{idComedien : dataComedien.idComedien}]}});
var data={abonnement:[]};
data.abonnement=dataFac;
var str_date= new Date(dataFac[0].timestampCreation.toString());
var nameFile= 'Fac_'+dataFac[0].idFacture+'_'+str_date.getFullYear().toString()+'-'+('0'+str_date.getMonth()+1).slice(-2).toString()+'-'+('0'+str_date.getDate()).slice(-2).toString()+'.pdf';
var path='public/upload/Comediens/'+dataComedien.idComedien.toString()+'/factures/'+nameFile;
createPdf.pdfGenerator(data,path);
return dataFac;
}
return null;
}
catch(e){
console.log(e);
return null;
}
}
cm_abonnements.remoteMethod(
'getAbonnementById',{
http:{
verb:'POST'
},
description:'Get detail facture by number facture',
accepts:[
{arg:"options", "type":"object","http":"optionsFromRequest"},
{ arg: 'req', type: 'object', 'http': {source: 'req'}},
{arg: 'res', type: 'object', 'http': {source: 'res'}}
],
returns:{arg:'data',root:true}
}
);
Any help would be appreciated. Thank you
You need to send the following HTTP headers:
Content-Type: application/pdf
Content-Disposition: inline; filename="download.pdf"
After data is generated and pdf file is stored, there are 2 steps left to implement the "download" feature:
Return HTTP response to browser, with Content-Type header as application/pdf, and Content-Disposition header as attachment; filename="yourname.pdf". Normally, this would be handled automatically by web framework. I'm not familiar with loopback, so take Express for example:
In generatorPdf.js, add a callback to listen the finish event:
pdfGenerator:function(data, pathfile, callback){
...
pdfDoc.pipe(fs.createWriteStream(pathfile)).on('finish', callback);
...
}
When pdfGenerator function is used, pass a callback function parameter. If the pdf work is "finished", return response to browser using res.download() (It's Express API, but I believe loopback has similar API as loopback is built on top of Express):
var nameFile=...
var path=...
createPdf.pdfGenerator(data, path, function() {
res.download(path, nameFile);
});
In browser side, if it's an AJAX request (I guess so, as you mentioned it is a POST request), you need to handle the request with some blob operation. Here is an example snippet, with explanation comment:
var req = new XMLHttpRequest();
req.open('POST', '/download', true); // Open an async AJAX request.
req.setRequestHeader('Content-Type', 'application/json'); // Send JSON data
req.responseType = 'blob'; // Define the expected data as blob
req.onreadystatechange = function () {
if (req.readyState === 4) {
if (req.status === 200) { // When data is received successfully
var data = req.response;
var defaultFilename = 'default.pdf';
// Or, you can get filename sent from backend through req.getResponseHeader('Content-Disposition')
if (typeof window.navigator.msSaveBlob === 'function') {
// If it is IE that support download blob directly.
window.navigator.msSaveBlob(data, defaultFilename);
} else {
var blob = data;
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = defaultFilename;
document.body.appendChild(link);
link.click(); // create an <a> element and simulate the click operation.
}
}
}
};
req.send(JSON.stringify({test: 'test'}));
var address = [ "data/somedata1.json", "data/somedata2.json", "data/somedata3.json", "data/somedata4.json", "data/somedata5.json"];
and function to import this file
function readData()
{
var loadFile = function (filePath, done)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", filePath, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onload = function () { return done(this.responseText) }
xhr.send();
}
address.forEach(function (file, i)
{
loadFile(file, function (responseText)
{
jsonData[i] = JSON.parse(responseText);
if(i === 4)
{
fill(jsonData);
document.getElementById("el").innerHTML = jsonData[2].title3;
Dosometing(jsonData[0])
}
})
})
}
All JSON files have absolute 150kb. Problem is, sometimes when I run this code on website I get jsonData[0] undefinded and sometimes all load success. It means all data are not load properly. What im doing wrong ? There is any chance to write this code better to make sure all files are loaded properly ?
One issue is that even for small files it is not guaranteed, that the downloads finish in order.
It would be better to keep track of the finished download count with a separate variable:
function readData() {
var loadFile = function(filePath, done) {
var xhr = new XMLHttpRequest();
xhr.open("GET", filePath, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onload = function() { return done(this.responseText) }
xhr.send();
}
var finishedCount = 0;
address.forEach(function(file, i) {
loadFile(file, function(responseText) {
jsonData[i] = JSON.parse(responseText);
finishedCount++;
if(finishedCount === address.length) {
fill(jsonData[4]);
document.getElementById("el").innerHTML = jsonData[2].title3;
Dosometing(jsonData[0])
}
});
})
}
I am trying to :
Send a zip file via xmlhttp to the client
then read the file using zip.js and render its contents
I successfully receive the binary of the file i.e. the success callback is called but I get and error when I try to do getEntries. I think the error is with the way of sending stream , please help.
Error msg :
Error in reading zip file
My client side code (using angular) :
$http.get(window.location.origin + '/book/'+bookName,{responseType:"Blob"}).
success(function (data , error) {
var a = new Uint8Array(data);
//var dataView = new DataView(data);
//var blob = new Blob(dataView.buffer);
zip.useWebWorkers = true;
zip.workerScriptsPath = '/js/app/';
zip.createReader(new zip.BlobReader(data), function(reader) {
// get all entries from the zip
reader.getEntries(function(entries) { //HERE I GET THE ERROR
if (entries.length) {
// get first entry content as text
entries[0].getData(new zip.TextWriter(), function(text) {
// text contains the entry data as a String
console.log(text);
// close the zip reader
reader.close(function() {
// onclose callback
var a = 0;
});
}, function(current, total) {
// onprogress callback
var a = 0;
});
}
});
},
function(error) {
// onerror callback
var a = 0;
});
})
.error( function (data , error) {
var a = 0;
});
My Server side code on Node:
router.get('/book/:bookName',function (req , res ) {
console.log('Inside book reading block : ' + req.params.bookName);
req.params.bookName += '.zip';
var filePath = path.join(__dirname,'/../\\public\\books\\' ,req.params.bookName );
var stat = fileSystem.statSync(filePath);
res.writeHead(200, {
//'Content-Type': 'application/zip',
'Content-Type': 'blob',
'Content-Length': stat.size
});
var readStream = fileSystem.createReadStream(filePath);
// replace all the event handlers with a simple call to readStream.pipe()
readStream.pipe(res);
});
It is probable that you might have already found a solution. I faced the same problem today and this is how I solved it in plain javascript:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'assets/object/sample.zip', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
// response is unsigned 8 bit integer
var responseArray = new Uint8Array(this.response);
var blobData = new Blob([responseArray], {
type: 'application/zip'
});
zip.createReader(new zip.BlobReader(blobData), function(zipReader) {
zipReader.getEntries(displayEntries);
}, onerror);
};
xhr.send();
The problem I see in your code is that you are changing the value to Uint8Array and assigning it to var a, but still use the raw data in blobreader. Also the blob reader required blob and not an array. So you should have converted var a into blob and then used it for reading.
I am using the js api for parse.com from an Angular js app. I am trying to save / update the profile picture of the user. I've got the following code:
Some html . . .
<input type="file" capture="camera" accept="image/*" id="profilePhotoFileUpload">
Thansk to google i/o and raymond and parse js guide
code in my controller:
$scope.updateUserProfile = function (user) {
var fileUploadControl = $("#profilePhotoFileUpload")[0];
if (fileUploadControl.files.length > 0) {
var file = fileUploadControl.files[0];
var name = "photo.jpg";
var parseFile = new Parse.File(name, file);
parseFile.save();
}
I keep getting this error:
TypeError: Cannot call method 'then' of undefined
at Object.Parse.File.save (parse-1.2.8.js:4084:43)
when calling parseFile.save()
basically the _source is undefined . . . why?!
Thanks!
I finally work it around, since my ultimate goal was to use it with phonegap, with the info in this post. . Big thanks to Raymond Camden!
function gotPic(data) {
window.resolveLocalFileSystemURI(data, function(entry) {
var reader = new FileReader();
reader.onloadend = function(evt) {
var byteArray = new Uint8Array(evt.target.result);
var output = new Array( byteArray.length );
var i = 0;
var n = output.length;
while( i < n ) {
output[i] = byteArray[i];
i++;
}
var parseFile = new Parse.File("mypic.jpg", output);
parseFile.save().then(function(ob) {
navigator.notification.alert("Got it!", null);
console.log(JSON.stringify(ob));
}, function(error) {
console.log("Error");
console.log(error);
});
}
reader.onerror = function(evt) {
console.log('read error');
console.log(JSON.stringify(evt));
}
entry.file(function(s) {
reader.readAsArrayBuffer(s);
}, function(e) {
console.log('ee');
});
});
}
If anyone else is working Phonegap and Parse, here is another helpful example from Raymond Camden, that takes a picture:
http://www.raymondcamden.com/2013/07/23/better-example-of-phonegap-parse-and-uploading-files
var imagedata = "";
$("#takePicBtn").on("click", function(e) {
e.preventDefault();
navigator.camera.getPicture(gotPic, failHandler,
{quality:50, destinationType:navigator.camera.DestinationType.DATA_URL,
sourceType:navigator.camera.PictureSourceType.PHOTOLIBRARY});
});
function gotPic(data) {
console.log('got here');
imagedata = data;
$("#takePicBtn").text("Picture Taken!").button("refresh");
}
and it gets saved like:
var parseFile = new Parse.File("mypic.jpg", {base64:imagedata});
console.log(parseFile);
parseFile.save().then(function() {
var note = new NoteOb();
note.set("text",noteText);
note.set("picture",parseFile);
note.save(null, {
success:function(ob) {
$.mobile.changePage("#home");
}, error:function(e) {
console.log("Oh crap", e);
}
});
cleanUp();
}, function(error) {
console.log("Error");
console.log(error);
});
Especially note the {base64:imagedata}, as this is the key for creating the parse file using string data like this.