0

I want to use fileSaver.js to click a button and save a binary blob (type: "application/octet-stream"). But i don´t know how to save the blob the way i need it to be.

The final file (example.mod) needs to have the following sequence:

  • 8 (the number of bytes for the next peace of information, integer number)
  • <POINTS> (a tag name with 8 characters)
  • 3 (the number of points)
  • [A,10.5,10,10] (a point: an array with a letter and 3 floating numbers)
  • [B,20,10,0.7] (a point: an array with a letter and 3 floating numbers)
  • [C,10,20.3,10] (a point: an array with a letter and 3 floating numbers)
  • 9 (the number of bytes for the next peace of information, integer number)
  • </POINTS> (a tag name with 9 characters)

I´ve found some examples saving "text/plain" or "image/png", but nothing that I could figure out how to apply in my case.

I've made this CodePen showing what I´m trying to do, but the resulting file is not what expected! it returns a file like that (all the information is visible as if was a text file):

8<POINTS>3A,10.5,10,10B,20,10,0.7C,10,20.3,109</POINTS>

...but what I would like to get is the file writen in Bytes, something like this (when looked at in a text-editor):

enter image description here

...or when read it as an ArrayBuffer I should get something like this result:

Int8Array(119)[
    0: 0
    1: 0
    2: 0
    3: 8
    4: 60
    5: 80
    6: 79
    7: 73
    8: 78
    9: 84
    10: 83
    11: 62
    12: 3
    13: 0
    14: 0
    15: 0
    16: 2
    17: 0
    18: 0
    19: 0
    20: 1
    21: 65
    22: 0
    23: 0
    24: 0
    25: 0
    26: 0
    27: 0
    28: 37
    29: 64
    30: 0
    31: 0
    32: 0
    33: 0
    34: 0
    35: 0
    36: 36
    37: 64
    38: 0
    39: 0
    40: 0
    41: 0
    42: 0
    43: 0
    44: 36
    45: 64
    46: 2
    47: 0
    48: 0
    49: 0
    50: 1
    51: 66
    52: 0
    53: 0
    54: 0
    55: 0
    56: 0
    57: 0
    ​​58: 52
    ​​59: 64
    ​​60: 0
    ​​61: 0
    ​​62: 0
    ​​63: 0
    ​​64: 0
    65: 0
    66: 36
    ​​67: 64
    ​​68: 102
    ​​69: 102
    ​​70: 102
    ​​71: 102
    72: 102
    ​​73: 102
    ​​74: -26
    ​​75: 63
    76: 2
    77: 0
    ​​78: 0
    79: 0
    ​​80: 1
    ​​81: 67
    ​​82: 0
    ​​83: 0
    ​​84: 0
    ​​85: 0
    ​​86: 0
    87: 0
    ​​88: 36
    ​​89: 64
    ​​90: -51
    ​​91: -52
    ​​92: -52
    ​​93: -52
    ​​94: -52
    ​​95: 76
    96: 52
    ​​97: 64
    98: 0
    ​​99: 0
    100: 0
    ​​101: 0
    ​​102: 0
    ​​103: 0
    ​​104: 36
    105: 64
    ​​106: 0
    107: 0
    108: 0
    ​​109: 9
    ​​110: 60
    ​​111: 47
    ​​112: 80
    ​​113: 79
    ​​114: 73
    ​​115: 78
    ​​116: 84
    ​​117: 83
    ​​118: 62
]

PS: If you know how to get those results without using filesaver.js, that would work for me as well.

Any help will be most appreciated!

Thank you.

Serginho
  • 39
  • 10
  • I am trying to prepare the data with Arraybuffer e typed arrays before creating the blob, but with no success so far. Really lost here! :( – Serginho Feb 01 '21 at 00:53

1 Answers1

0

Solved:

After digging for a while found in different questions the bits and peaces i needed to put together a solution (not sure if is the best practice, but is working!)

First I had to treat every kind of input (string, number(positive integer), number(signed floating real) in differnet ways to match the format I need in the file.

*1: from here got the StingToArrayBuffer solution

//string to ArrayBuffer
function str2ab(str) {
  var buf = new ArrayBuffer(str.length); // 2 bytes for each char
  var bufView = new Uint8Array(buf);
  for (var i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  var result = new Uint8Array(bufView.buffer);
  return result;
}

*2: From that answer got the numberToArrayBuffer solution, modified to my needs!

//.............number to ArratBuffer //*2
//integer To ArrayBuffer - 4 bits
function int2ArrBuf(value) {
  const view = new DataView(new ArrayBuffer(4))
  for (var index = 3; index >= 0; --index) {
    view.setUint8(index, value % 256)
    value = value >> 8;
  }
  var int = new Uint8Array(view.buffer);
  return int
}

//Reverse order
function Xint2ArrBuf(value) {
  const view = new DataView(new ArrayBuffer(4))
  for (var index = 0; index <= 3; ++index) {
    view.setUint8(index, value % 256)
    value = value >> 8;
  }
  var int = new Uint8Array(view.buffer);
  return int
}
//......................

//one bit long integer
function strsize(value) {
  const view = new DataView(new ArrayBuffer(1))
  
    view.setUint8(0, value % 256)
    value = value >> 8;
 
  var result= new Uint8Array(view.buffer);
  return result
}

*3: from this one, figure how to save the point coordinates

//point To ArrayBuffer - 3x 8 bits
function Pt2ArrBuf(x,y,z){
  var float32 = new Float32Array(3);
  float32[0] = x;
  float32[1] = y;
  float32[2] = z;

  var f = new Float64Array(float32);
  var pt = new Int8Array(f.buffer);  
  
  return pt;
}

*4: preparing the data sequence and concatenating the TypedArrays

//here I write the file the way i Need it to be...

const binaryData = [
    int2ArrBuf(9),
    strsize(8),
    str2ab('<POINTS>'),
    int2ArrBuf(3),
    Xint2ArrBuf(2),
    strsize(1),
    str2ab('A'),
    Pt2ArrBuf(10.5,10,10),
    Xint2ArrBuf(2),
    strsize(1),
    str2ab('B'),
    Pt2ArrBuf(-20,10,0.7),
    int2ArrBuf(10),
    strsize(9),
    str2ab('</POINTS>') 
  ];

//here I join the many TypedArrays into one...
const mergedInt8Array = new Int8Array(binaryData.map(typedArray => [...new Int8Array(typedArray.buffer)]).flat());
console.log(mergedInt8Array);

*5: Codepen from David

*6: Correction on the blob syntax

//*5:direct download via FileSaver.js
var SaveButton = document.getElementById('save');
SaveButton.addEventListener('click', function () {
  var filename = document.getElementById("name").value;
  filename += '.txt';
  var blob = new Blob([mergedInt8Array], { type: "application/octet-stream"}); //*6
  saveAs(blob, filename);
});

Here is the Codepen with the Solution. Thank You.

Serginho
  • 39
  • 10