0

I have a 2D array (context: JavaScript, Google Chrome extension), each index has text consisting all kind of characters, I can convert this 2D-array into a csv file and download it using below code:

function Download(){
  //https://stackoverflow.com/a/14966131/11974735
  var array = JSON.parse(sessionStorage.getItem("array"));
  let csvContent = "data:text/csv;charset=utf-8," 
    + array.map(e => e.join(",")).join("\n");
  var encodedUri = encodeURI(csvContent);
  var link = document.createElement("a");
  link.setAttribute("href", encodedUri);
  link.setAttribute("download", "my.csv");
  document.body.appendChild(link); // Required for FF
  link.click();
}
// This will download the data file named "my_data.csv".

But how can I upload and use it as 2D-array again (in another machine, in case of hard drive crash)? I searched on internet, but the solution presented have conditions like the file can not consist a specific character (delimiter issue?), and other issues I could not get.

So can anyone help?

This basic solution falls apart if your cells contain quotes, commas or other escaped characters. To address more complex CSV strings, you'd have to implement a RegEx solution (see accepted answer to How can I parse a CSV string with Javascript?); and to support multiple common formats, you'd be better off just using a library.

Konrad
  • 21,590
  • 4
  • 28
  • 64
  • Why can't you use that array twice? You're creating the array from a sessionStorage element, so as long as that element exists you should be able to call the "Download" function as many times as you want. – Robbi Aug 06 '22 at 20:25
  • @Robbi simple, to use later, after rebooting computer and also, for backup. – Consider Non-Trivial Cases Aug 06 '22 at 20:33
  • you have to store that item in localStorage not in sessionStorage – Robbi Aug 06 '22 at 20:47
  • @Robbi you are missing the simple point, what if one wants to use it another machine? or there is a hard drive cash? – Consider Non-Trivial Cases Aug 07 '22 at 10:45
  • 1
    It is not I who am missing something, it is you who are not able to "ask a question" in the right way. You already have the backup because you just downloaded a csv file. In order to upload this backup to other machines you will need to create a tool that reads this csv file and stores the contents in persistent storage (localStorage, chrome.storage, indexedDB). – Robbi Aug 07 '22 at 15:53
  • 1
    Please do not delete questions when you got answers. At least not without giving feedback on why you delete and whether you are going to undelete again (which is an appreciated way to go, if you e.g. want to improve a question based on feedback you got). – Yunnosch Aug 08 '22 at 06:09
  • And please find out about the reasons and benefits of proper indentation. – Yunnosch Aug 08 '22 at 06:10
  • 1
    @ConsiderNon-TrivialCases what exactly is the problem with uploading the file? – Konrad Aug 08 '22 at 23:18
  • @KonradLinkowski please read the last paragraph of the post. – Consider Non-Trivial Cases Aug 09 '22 at 11:20
  • @ConsiderNon-TrivialCases What is inside the 2D array from `getItem("array")`. I mean is it all just numbers or is it mixed content? If numbers... What is the highest number in this array (or even, does it range between some 2 min/max values)? – VC.One Aug 09 '22 at 13:15
  • I still don't understand how do you upload this file. Do you just copy the content and paste it into the session storage? – Konrad Aug 09 '22 at 15:49
  • @VC.One each entry of the array is text, the text contains all type of Characters including special character like !, @, #, #, $, new line space, numbers etc – Consider Non-Trivial Cases Aug 09 '22 at 18:28
  • @KonradLinkowski that is what I am asking for, how to upload, I know how to download,... and upload means uploading a text/csv file...like attaching file in email.... the HTML file will ask for upload path in the computer and then upload file from there – Consider Non-Trivial Cases Aug 09 '22 at 18:30
  • https://stackoverflow.com/questions/750032/reading-file-contents-on-the-client-side-in-javascript-in-various-browsers – Konrad Aug 09 '22 at 18:31

1 Answers1

0

We can use JSON.stringify to create CSV line strings, and JSON.parse to restore it back.

Example:

var a = ["abc", '"', "'", ","];
var csv = JSON.stringify(a);     // format any symbols properly
console.log(csv);                // ["abc","\"","'",","]
csv = csv.slice(1, -1);          // remove the square brackets
console.log(csv);                // "abc","\"","'",","
csv = csv.replace(/\\"/g, '""'); // replace \" with ""
console.log(csv);                // "abc","""","'",","

Here is the complete code for downloading as CSV file from sessionStorage, and uploading the file and store it back to the sessionStorage.

<html>
<head>
  <meta charset="utf-8" />
</head>
<body>
<script>

storeData(); // initially store data, for download

function storeData() {
  var array = JSON.stringify([
    ["lorem", "symbols\"',\n!@#$%^&*()_+-={}|[]:;<>/?", 'ip"sum'],  // any symbols
    ["hello", "emoji⌚⌛⏩", "world"],  // any emojis
  ]);
  sessionStorage.setItem("array", array);
}

function Download() {
  var array = JSON.parse(sessionStorage.getItem("array"));
  // use JSON.stringify per sub array, remove the square brackets to create comma separated string, replace all \" to ""
  var csvContent = array.map(e => JSON.stringify(e).slice(1, -1).replace(/\\"/g, '""')).join("\n");
  var encodedUri = "data:text/csv;charset=utf-8," + encodeURIComponent(csvContent);
  var link = document.createElement("a");
  link.setAttribute("href", encodedUri);
  link.setAttribute("download", "my.csv");
  document.body.appendChild(link); // Required for FF
  link.click();
  // This will download the data file named "my_data.csv".
}

function Upload() {
  var file = document.getElementById("fileForUpload").files[0];
  if (file) {
    var reader = new FileReader();
    reader.readAsText(file, "UTF-8");
    reader.onload = function (evt) {
      var array = convertCsvToArray(evt.target.result);       
      sessionStorage.setItem("array", JSON.stringify(array));
    }
    reader.onerror = function (evt) {
      console.log("error reading file");
    }
  }
}

function convertCsvToArray(csvStr) {
  // Processing to accept any valid CSV string.
  return csvStr.split(/\n/).map(line => { // split per line
    var items = Array.from(
      line.matchAll(/("(([^"]|"")*?)"|([^",][^,]*?))?(,|$)/g)
    ).map(match => match[2] || match[4] || ''); // match[3] are double-quoted values, match[5] are bare values

    // Replace all " or "" to \", wrap with double-quotes, add square brackets to construct valid json string
    var jsonStr = '[' + items.slice(0, -1).map(item => '"' + item.replace(/""?/g, '\\"') + '"').join(",") + ']';

    // Use JSON.parse to convert back to array
    return JSON.parse(jsonStr);
  });
}

</script>
  <button onclick="Download()">Download</button>
  <input type="file" id="fileForUpload">
  <button onclick="Upload()">Upload</button>
</body>
</html>
elfan
  • 1,131
  • 6
  • 11
  • There is an issue, after downloading, if one edits in any cell of the CSV, and then upload, the file can't be read... gives error .. `VM22:1 Uncaught SyntaxError: Unexpected token 'M', "[Medical,"W"... is not valid JSON` – Consider Non-Trivial Cases Aug 15 '22 at 16:30
  • I got the impression that you will use the downloaded CSV file for backup only, not for manual editing. With this JSON approach, if edited manually or by other tools, every value must be wrapped by double quotes, e.g. the `Medical` should be `"Medical"`. I can add more processing if we also need to support bare values. – elfan Aug 15 '22 at 23:19
  • Please do that, I am awarding you the bounty as it nears to the end, but I request you to add the processing that allows reading the edited file, after uploading, I am sorry I was not clear enough, thanks for your help. – Consider Non-Trivial Cases Aug 16 '22 at 07:35
  • Thank you. I have updated it with `convertCsvToArray()` for processing any valid CSV format. Let me know if there are still unhandled cases. – elfan Aug 17 '22 at 16:06
  • @ConsiderNon-TrivialCases I have updated the code a little bit to allow empty value between commas. If it already fulfils all your needs, please accept it as the answer. – elfan Aug 23 '22 at 02:46