8

I need some help getting my head around how the file is accessed in JavaScript to do some operations on it.

I would like to loop through a file byte by byte using JavaScript. I can already select which file I would like to read. And I can read preset byte of the file.

I've found this nice example on how to read a slice of a file here:

http://www.html5rocks.com/en/tutorials/file/dndfiles/

Here is the snippet of code which I'm playing with:

<style>
  #byte_content {
    margin: 5px 0;
    max-height: 100px;
    overflow-y: auto;
    overflow-x: hidden;
  }
  #byte_range { margin-top: 5px; }
</style>

<input type="file" id="files" name="file" /> Read bytes: 
<span class="readBytesButtons">
  <button data-startbyte="0" data-endbyte="4">1-5</button>
  <button data-startbyte="5" data-endbyte="14">6-15</button>
  <button data-startbyte="6" data-endbyte="7">7-8</button>
  <button>entire file</button>
</span>
<div id="byte_range"></div>
<div id="byte_content"></div>

<script>
  function readBlob(opt_startByte, opt_stopByte) {

    var files = document.getElementById('files').files;
    if (!files.length) {
      alert('Please select a file!');
      return;
    }

    var file = files[0];
    var start = parseInt(opt_startByte) || 0;
    var stop = parseInt(opt_stopByte) || file.size - 1;

    var reader = new FileReader();

    // If we use onloadend, we need to check the readyState.
    reader.onloadend = function(evt) {
      if (evt.target.readyState == FileReader.DONE) { // DONE == 2
        document.getElementById('byte_content').textContent = evt.target.result;
        document.getElementById('byte_range').textContent = 
            ['Read bytes: ', start + 1, ' - ', stop + 1,
             ' of ', file.size, ' byte file'].join('');
      }
    };

    var blob = file.slice(start, stop + 1);
    reader.readAsBinaryString(blob);
  }

  document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {
    if (evt.target.tagName.toLowerCase() == 'button') {
      var startByte = evt.target.getAttribute('data-startbyte');
      var endByte = evt.target.getAttribute('data-endbyte');
      readBlob(startByte, endByte);
    }
  }, false);
</script>

Now I would like to loop through the file, four bytes at a time, but cannot seem to figure out how to do that. The reader does not seem to allow me to read more than once.

Once I can read from the file more than once, I should be able to iterate through it quite easily with something like this:

while( placemark != fileSize-4 ){
    output = file.slice(placemark, placemark + 4);      
    console.log(output);
    placemark = placemark + 5;
    }

Thanks in advance! Here is a link to a jsFiddle and plnkr version

9
  • 1
    If you can, I would try and refactor out your DOM manipulation code from your file reading code. This way if you have a clear separation of concerns it will be easier to debug and make changes to. Commented Jan 13, 2015 at 10:21
  • I agree! I feel that once that's done, the rest should be easy... But, I'm not sure how? Commented Jan 13, 2015 at 10:24
  • put it into a plnkr then I'll have a look at refactoring it, then solving it Commented Jan 13, 2015 at 10:26
  • Okay... added fiddling options. Commented Jan 13, 2015 at 11:07
  • If I could get around the "already busy reading Blobs" issue, I'm guessing that could also help? Commented Jan 13, 2015 at 11:29

3 Answers 3

4

I'm not sure it is what you wanted but maybe it can help, and anyway I had fun.
I tried setting reader and file vars as global :

var reader = new FileReader(), step = 4, stop = step, start = 0, file;

document.getElementById('files').addEventListener('change', load, true);

function load() {
  var files = document.getElementById('files').files;
  file = files[0];
  reader.onloadend = function(evt) {
    if (evt.target.readyState == FileReader.DONE) {
      var result = evt.target.result;
      document.getElementById('byte_content').textContent += result; 
      document.getElementById('byte_range').textContent = ['Read bytes: ', start, ' - ', start+result.length,
        ' of ', file.size, ' byte file'
      ].join('');
    }
  }
}

function next() {
  if (!file) {
    alert('Please select a file!');
    return;
  }
  var blob = file.slice(start, stop);
  reader.readAsBinaryString(blob);

  start+= step;
  stop = start+step;
}

function loop() {
  if (!file) {
    alert('Please select a file!');
    return;
  }
  if (start < file.size) {
    next();
    setTimeout(loop, 50);
  }
}
<input type="file" id="files" name="file" />Read bytes:
<span class="readBytesButtons">
  <button onclick="next()">next</button>
  <button onclick="loop()">loop</button>
</span>
<div id="byte_range"></div>
<div id="byte_content"></div>

Sign up to request clarification or add additional context in comments.

1 Comment

Brilliant. Yes. That works great, thank you. I guess it makes sense that those vars global.
1

I'd read the blob as an ArrayBuffer and use a DataView to read through the data

  function readBlob(opt_startByte, opt_stopByte) {

    var files = document.getElementById('files').files;
    if (!files.length) {
      alert('Please select a file!');
      return;
    }

    var file = files[0];
    var start = parseInt(opt_startByte) || 0;
    var stop = parseInt(opt_stopByte) || file.size - 1;

    var reader = new FileReader();

    reader.onload = function(evt) {
        var placemark = 0, dv = new DataView(this.result), limit = dv.byteLength - 4, output;
        while( placemark <= limit ){
            output = dv.getUint32(placemark);      
            console.log(' 0x'+("00000000" + output.toString(16)).slice(-8));
            placemark += 4;
        }     
    };

    var blob = file.slice(start, stop + 1);
    reader.readAsArrayBuffer(blob);
  }
<input type="file" id="files" onchange="readBlob(0, 100)">

1 Comment

Thank you! This is definitely also an answer.
-2

In the onload handler of FileReader, convert the result to string (toString()), then read 4 chars at a time with the string's slice method.

var contents = null;

reader.onload = function(){
    contents = reader.result.toString();
}

var startByte = 0;

// read 4 bytes at a time
var step = 4;

// actual reading (doesn't alter the contents object)
console.log(contents.slice(startByte, step))

// update the next startByte position
startByte += step;

2 Comments

4 bytes != 4 characters
Yeah, that would be a cute little solution, if I didn't need the actual bytes- Like @Justinas mentioned. Thank you both, though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.