I'm trying to get a script to work, which is called sfo.js.
The repo mentions only this usage:
keys = parse_sfo(Some_ArrayBuffer);
console.log(keys['TITLE']);
Looking into the sfo.js, parse_sfo has the sfoBytes argument.
From this I've concluded the sfoBytes argument needs to be an arraybuffer of file bytes.
I've tried to make a script that parses the SFO file into a array of bytes:
<script src="sfo.js"></script>
<script>
function stringToArrayBuffer(str) {
var buf = [];
for (var i=0, strLen=str.length; i<strLen; i++) {
buf[i] = str.charCodeAt(i);
}
console.log(buf);
return buf;
}
function testing(url) {
var request = new XMLHttpRequest();
request.open('GET', url, false);
request.send(null);
if (request.status === 200) {
console.log(request.response);
var response = request.response;
var array = stringToArrayBuffer(response);
return array;
} else {
alert('Error!');
}
}
var data = testing('param.sfo');
var sfo = parse_sfo(data);
</script>
That throws an error in the console:
Uncaught RangeError: byte length of Uint32Array should be a multiple of 4 at new Uint32Array (<anonymous>)
at readUint32At (sfo.js:20)
at parse_sfo (sfo.js:113)
at (index):29
I'm pretty sure I'm doing something wrong. Does anybody understand how I can make the script work properly?
I have a sample file for param.sfo: https://filebin.net/gghosrp6u93jn7y8 (if linking to a download is not allowed please let me know)
Ok, finally the working example.
I've found a small param.sfo file in the internet.
Notes:
there are 2 versions of file reader: local and remote
in the snippet below you can test both (I've added my param.sfo as an external link to test 'remote'). To test 'local' you just need to select any sfo file from you PC.
as a result I show all the keys, not only 'TITLE' (as it is in your question). You can then select desired key
function getSfoLocal(callback) {
// for now I use local file for testing
document.querySelector('input').addEventListener('change', function() {
var reader = new FileReader();
reader.onload = function() {
var arrayBuffer = this.result;
var array = new Uint8Array(arrayBuffer);
// var binaryString = String.fromCharCode.apply(null, array);
if (typeof callback === 'function') callback(array);
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
}
function getSfoRemote(url, callback) {
var concatArrayBuffers = function(buffer1, buffer2) {
if (!buffer1) {
return buffer2;
} else if (!buffer2) {
return buffer1;
}
var tmp = new Uint8Array(buffer1.length + buffer2.length);
tmp.set(buffer1, 0);
tmp.set(buffer2, buffer1.byteLength);
return tmp.buffer;
};
fetch(url).then(res => {
const reader = res.body.getReader();
let charsReceived = 0;
let result = new Uint8Array;
reader.read().then(function processText({
done,
value
}) {
// Result objects contain two properties:
// done - true if the stream has already given you all its data.
// value - some data. Always undefined when done is true.
if (done) {
if (typeof callback === 'function') callback(result);
return result;
}
// value for fetch streams is a Uint8Array
charsReceived += value.length;
const chunk = value;
result = concatArrayBuffers(result, chunk);
// Read some more, and call this function again
return reader.read().then(processText);
});
});
}
function getSfo(type, url, callback) {
if (type === 'local') getSfoLocal(callback);
if (type === 'remote') getSfoRemote(url, callback);
}
getSfo('local', null, (data) => {
keys = parse_sfo(data);
console.log('LOCAL =', keys);
});
function goremote() {
getSfo('remote', 'https://srv-file9.gofile.io/download/Y0gVfw/PARAM.SFO', (data) => {
keys = parse_sfo(data);
console.log('REMOTE =', keys);
});
}
div { padding: 4px; }
<!--script src="https://rawcdn.githack.com/KuromeSan/sfo.js/c7aa8209785cc5a39c4231e683f6a2d1b1e91153/sfo.js" for="release"></script-->
<script src="https://raw.githack.com/KuromeSan/sfo.js/master/sfo.js" for="develop"></script>
<div>Local version: <input type="file" /></div>
<div>Remote version: <button onclick="goremote()">Go remote</button></div>
P.S. It seems that gofile.io service that I've used for remote example is not visible sometimes. If you have permanent link to param.sfo just let me know. But now I have to visit my file, and only after that it becomes visible.
You are using some 20 years old JavaScript.
Please use fetch add TextEncoder for your own sanity.
fetch(url).then(res => res.json().then(data => {
const encoder = new TextEncoder()
const bytes = encoder.encode(data)
parse_sfo(data)
})
sfoBytes should be the actual array buffer of the sfo file. It's simpler than what you are trying, you shouldn't need to convert the request response with stringToArrayBuffer since you can get an array buffer from XMLHttpRequest. Also, the SFO file isn't a text file, it's binary, so conversion from string wouldn't work anyway.
Changing your request to get a arraybuffer response type should do it like this:
function testing(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = "arraybuffer";
request.send(null);
if (request.status === 200) {
console.log(request.response);
var response = request.response;
var sfo = parse_sfo(response);
} else {
alert('Error!');
}
}
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'}));
I've got a problem with a javascript Filereader which returns the error Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.
Here's the javascript:
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET", "C:\\Users\\yw1kew\\Desktop\\LG_FRAME.plmx");
xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
xhr.onload = function()
{
blob = xhr.response;//xhr.response is now a blob object
}
xhr.send();
var myReader = new FileReader();
myReader.readAsArrayBuffer(blob) // THE CODE FAILS HERE
Any idea ? Thanks
I just had the same problem and after a few hours of searching the solutions online, I realised I might just be able to use fetch, which does indeed work !
fetch("./file.bin")
.then((data) => {
return data.arrayBuffer();
})
.then((array) => {
console.log(buffer);
})
.catch((error) => {
console.log(error);
});
In my case I use SharePoint as a platform and I needed to convert the binary code like this:
binaryDecode = function (data) {
var ret = '';
if (data) {
var byteArray = new Uint8Array(data);
for (var i = 0; i < data.byteLength; i++) {
ret = ret + String.fromCharCode(byteArray[i]);
}
}
return ret;
};
Then use it like this:
console.log(binaryDecode(arrayBuffer));
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.
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.