8

I'm new to d3.js so I know this might seem as a silly question to some so please bear with me. I'm trying to parse a csv file which a user uploads and print it's output in the console. I'm able to parse the CSV file when I provide the absolute path of the CSV file but when I try doing the same with file upload functionality I'm not getting any output in the console..

Working Javascript Code..

    var dataset = [];
    d3.csv("sample.csv", function(data) {
    dataset = data.map(function(d) { return [ d["Title"], d["Category"], d["ASIN/ISBN"], d["Item Total"] ]; });
    console.log(dataset[0]);
    console.log(dataset.length);
    }); 

Console Output...

["Men's Brooks Ghost 8 Running Shoe Black/High Risk Red/Silver Size 11.5 M US", "Shoes", "B00QH1KYV6", "$120.00 "]
 8

New HTML code..

    <input type="file" id="csvfile" name="uploadCSV"/>
    <br/>
    <button onclick="howdy()">submit</button>

Modified Javascript Code(not working)..

    var myfile = $("#csvfile").prop('files')[0];
    var reader = new FileReader();

    reader.onload = function(e) {
    var text = reader.result;
    }

    reader.readAsDataURL(myfile);

     var dataset = [];
    d3.csv(reader.result , function(data) {
    dataset = data.map(function(d) { return [ d["Title"], d["Category"], d["ASIN/ISBN"], d["Item Total"] ]; });
    console.log(dataset[0]);
    console.log(dataset.length);
    })

Since there was no official documentation on how to handle user uploaded CSV file I can't figure out where I'm going wrong..Is there a way I can use HTML5 file reader?? Please help..

Lucy
  • 1,812
  • 15
  • 55
  • 93
  • The problem is that this line `$("#csvfile")[0].files` does not give you the content of the file. You need to upload your file to your server first, and return a URL of the uploaded file. You then need to update your client side code(javascript) to use the uploaded file URL and not just the content of the file upload input – Tjaart van der Walt Mar 18 '16 at 08:26
  • Can't we use the HTML5 File Reader API to do that?? – Lucy Mar 18 '16 at 08:44
  • Yes you can use the html5 Fie Reader API – Tjaart van der Walt Mar 18 '16 at 08:48
  • I edited my code above to use file reader to get a url but still I'm not getting any output in the console. Could you please look at my edited code above and tell me where I'm going wrong? – Lucy Mar 18 '16 at 10:28
  • please see https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL you need to access the data in the onload callback. You set the `text` variable but you never used it. You might have to call the `d3.csv()` inside the callback when the content of the file is available. – Tjaart van der Walt Mar 18 '16 at 10:38

3 Answers3

9

You are close but you don't need to and can't call d3.csv on a reader.result. d3.csv makes an async AJAX call to retrieve a CSV file from a server. You already have the file contents and just want to parse, so use d3.csv.parse.

Full working example:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>

<body>
  <input type="file" onchange="loadFile()" />

  <script>
    var reader = new FileReader();  
    
    function loadFile() {      
      var file = document.querySelector('input[type=file]').files[0];      
      reader.addEventListener("load", parseFile, false);
      if (file) {
        reader.readAsText(file);
      }      
    }
    
    function parseFile(){
      var doesColumnExist = false;
      var data = d3.csv.parse(reader.result, function(d){
        doesColumnExist = d.hasOwnProperty("someColumn");
        return d;   
      });
      console.log(doesColumnExist);
    }
  </script>
</body>

</html>
Mark
  • 106,305
  • 20
  • 172
  • 230
  • Is there a way to check whether a certain columns exist in CSV file inside parseFile() function?? – Lucy Mar 29 '16 at 09:00
1

This is for d3-csv@3

<!-- https://www.jsdelivr.com/package/npm/d3-dsv -->
<script src="https://cdn.jsdelivr.net/npm/d3-dsv@3.0.1/dist/d3-dsv.min.js" integrity="sha256-IrzYc2a3nTkfvgAyowm/WKmIGdVCMCcccPtz+Y2y6VI=" crossorigin="anonymous"></script>
<input type="file" accept=".csv">
<button>test button</button>

<script>
const testData = `owner,repo,"branch name"
foo,demo,master
boo,"js awesome",sha1123456
`
document.querySelector(`input`).onchange = async e => {
  const input = e.target
  const file = input.files[0]
  const reader = new FileReader()
  reader.readAsText(new Blob(
    [file],
    {"type": file.type}
  ))
  const fileContent = await new Promise(resolve => {
    reader.onloadend = (event) => {
      resolve(event.target.result)
    }
  })
  const csvData = d3.csvParse(fileContent)
  console.log(csvData)
}

document.querySelector(`button`).onclick = e => {
  const csvData =  d3.csvParse(testData)
  console.log(csvData)
}
</script>

The below link may help you know the implementation of csvParse


If you just load the CSV only then do not import the whole JS. (instead of the d3-csv.js)

https://cdn.jsdelivr.net/npm/d3@7.0.1/dist/d3.min.js

https://cdn.jsdelivr.net/npm/d3-dsv@3.0.1/dist/d3-dsv.min.js

Carson
  • 6,105
  • 2
  • 37
  • 45
0

This is an old question and I think we have to clarify some points.

  1. How to load a local csv file
  2. How to link the loaded file with D3

1. Load a file is very simple just check this example:

const fileInput = document.getElementById('csv')
const readFile = e => {
  const reader = new FileReader()
  reader.onload = () => {
    document.getElementById('out').textContent = reader.result
  }
  reader.readAsBinaryString(fileInput.files[0])
}

fileInput.onchange = readFile
<div>
  <p>Select local CSV File:</p>
  <input id="csv" type="file" accept=".csv">
</div>
<pre id="out"><p>File contents will appear here</p></pre>

Here we have a simple input element with type="file" attribute, this lets us to pick a csv file. Then the readFile() function will be triggered whenever a file is selected and will call the onload function after reading the file as a binary string.

2. I recommend to use readAsDataURL() to integrate it with d3 like this:

const fileInput = document.getElementById('csv')
const previewCSVData = async dataurl => {
  const d = await d3.csv(dataurl)
  console.log(d)
}

const readFile = e => {
  const file = fileInput.files[0]
  const reader = new FileReader()
  reader.onload = () => {
    const dataUrl = reader.result;
    previewCSVData(dataUrl)
  }
  reader.readAsDataURL(file)
}

fileInput.onchange = readFile
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
  <p>Select local CSV File:</p>
  <input id="csv" type="file" accept=".csv">
</div>
<pre id="out"><p>File contents will appear here</p></pre>

To integrate the loaded file we call previewCSVData() and pass the file then we parse it using d3.csv() method. Also lets use await because it is an asynchronous call.

Note:

d3.csv internally uses fetch and works for any type of URL, (httpURL, dataURL, blobURL, etc...)

Teocci
  • 7,189
  • 1
  • 50
  • 48