HTML5 and Javascript file handling

  • Java
  • Thread starter jjc
  • Start date
  • #1
jjc
21
0

Main Question or Discussion Point

I am quite new to the JavaScript and HTML5 world, and I am having trouble with some code that I was hacking together (based on demo's from other people). I am trying to take a file as input (a binary image) and base64 encode it. I think I have the right function for the base64 encoding, I think it is just the handling of the file itself that I seem to be having trouble with. But...don't really know if that is ALL of my trouble. :)

Here is the code:

Code:
<script>
    //  From:  http://www.html5rocks.com/en/tutorials/file/dndfiles/
    //  JC update:  changing the handleFileSelect() function to do the base64 Processing
  
  function base64Encode(aFile) {
      /*
       * base64.js - Base64 encoding and decoding functions
       * See: http://developer.mozilla.org/en/docs/DOM:window.btoa
       *      http://developer.mozilla.org/en/docs/DOM:window.atob
       * Copyright (c) 2007, David Lindquist <david.lindquist@gmail.com>
       * Released under the MIT license
       *
       * JC, update:  Removed the 'atob' section of code; only need ENcoding, not DEcoding.
       */

      if (typeof btoa == 'undefined') {
          function btoa(str) {
              var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
              var encoded = [];
              var c = 0;
              while (c < str.length) {
                  var b0 = str.charCodeAt(c++);
                  var b1 = str.charCodeAt(c++);
                  var b2 = str.charCodeAt(c++);
                  var buf = (b0 << 16) + ((b1 || 0) << 8) + (b2 || 0);
                  var i0 = (buf & (63 << 18)) >> 18;
                  var i1 = (buf & (63 << 12)) >> 12;
                  var i2 = isNaN(b1) ? 64 : (buf & (63 << 6)) >> 6;
                  var i3 = isNaN(b2) ? 64 : (buf & 63);
                  encoded[encoded.length] = chars.charAt(i0);
                  encoded[encoded.length] = chars.charAt(i1);
                  encoded[encoded.length] = chars.charAt(i2);
                  encoded[encoded.length] = chars.charAt(i3);
              }
              return encoded.join('');
          }
      }
  }
  
  function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    var files = evt.dataTransfer.files; // FileList object - a FileList of File objects.
    var fReader = new FileReader () ;  
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      if ( !f.type.match('image.*')) { continue; }    //To skip non-image files
      fReader.onLoad = (function (aFile) { return base64Encode(aFile); } ) (f);
      output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
                  '<br><br>' , fReader.readAsBinaryString(f) , '<br><br>', '</li>');
     //This defines the 'onLoad' behavior/function...I think.
    
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
  }
  
  // Setup the dnd listeners.  [Slightly modified by JC]
  var dropZone = document.getElementById('drop_zone');
  dropZone.addEventListener('dragover', handleDragOver, false);
  dropZone.addEventListener('drop', handleFileSelect, false);
</script>
Any pointers would be appreciated.

Thanks,
J
 

Answers and Replies

  • #2
jjc
21
0
To narrow things down a bit, I guess my two main questions come from these lines:

Code:
fReader.onLoad = (function (aFile) { return base64Encode(aFile); } ) (f);
output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
     f.size, ' bytes, last modified: ',
     f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
     '<br><br>' , fReader.readAsBinaryString(f) , '<br><br>', '</li>');
In the first line, "fReader.onLoad...", I am not really certain what is going on with the (what I am guessing is) anonymous function and the trailing "...(f)...". Then in the second line, the calling of the file reader at that stage. And of course, the arguments of each call being in the right sequence.

There are probably other areas that I have messed up, but those are the ones that jump to mind.

Thanks,
J
 
Last edited by a moderator:
  • #3
33,733
5,422
In the first line, as you suspect, an anonymous function is being defined. This function is defined to have a single arg. The trailing (f) part is the actual argument to this function, probably the file that you intend to convert. This function is being assigned to the onLoad property of fReader, which suggests to me that it will be called when the page loads.

I'm not sure what's happening in the 2nd line. Could it be that fReader.readAsBinaryString causes onLoad to be called?

Anyway, that's what I think is happening.
 
  • #4
jjc
21
0
Ah, thanks! That trailing (f) was befuddling me.

So, to trace thing outloud...

The "fReader.OnLoad..." portion is the function definition (i.e. not actually doing anything at that time). The trailing "(f)" becomes the parameter to the function. What defines "f"? Does "f" come from the calling context, i.e. f = the file variable in the FOR loop? And then does the "f" correspond with the "aFile" variable/parameter in the function definition itself? In other words, the "f" is what I am passing to the function from the outer context, and "aFile" is the reference to that parameter used within the inner/function-definition context.

The "fReader.readAsBinaryString(f)" definition, I think, is what actually slurps up the file to read. When it is done reading, it should raise an 'onload' event, which would call that onload function just defined. I think. And then it is the 'onload' function which ends up running the encoding function I found.

Thanks for helping out; still a few more foggy bits to try and work out.

-- J
 
  • #5
jjc
21
0
OK, so here is my updated code, and down below that, the sample code that I am using as my template. There is a curious structure going on there that I don't quite get, and had disassembled slightly in my earlier version of code originally posted here. This new version of my code much more closely mimics the sample, but still doesn't work. Still kind of fumbling around in the dark; any suggestions appreciated.

My current code:

Code:
<script>
    //  From:  http://www.html5rocks.com/en/tutorials/file/dndfiles/
    //  JC update:  changing the handleFileSelect() function to do the base64 Processing
  
  function b64Enc(str) {
      {/*
       * base64.js - Base64 encoding and decoding functions
       * See: http://developer.mozilla.org/en/docs/DOM:window.btoa
       *      http://developer.mozilla.org/en/docs/DOM:window.atob
       * Copyright (c) 2007, David Lindquist <david.lindquist@gmail.com>
       * Released under the MIT license
       *
       * JC, update:  Removed the 'atob' section of code; only need ENcoding, not DEcoding.
       *     Removed 'btoa' naming wrapper-function; just expose the code directly in b64Enc()
       */}
      var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
      var encoded = [];
      var c = 0;
      while (c < str.length) {
          var b0 = str.charCodeAt(c++);
          var b1 = str.charCodeAt(c++);
          var b2 = str.charCodeAt(c++);
          var buf = (b0 << 16) + ((b1 || 0) << 8) + (b2 || 0);
          var i0 = (buf & (63 << 18)) >> 18;
          var i1 = (buf & (63 << 12)) >> 12;
          var i2 = isNaN(b1) ? 64 : (buf & (63 << 6)) >> 6;
          var i3 = isNaN(b2) ? 64 : (buf & 63);
          encoded[encoded.length] = chars.charAt(i0);
          encoded[encoded.length] = chars.charAt(i1);
          encoded[encoded.length] = chars.charAt(i2);
          encoded[encoded.length] = chars.charAt(i3);
      }
      alert ( "Done" ) ;   //OK, it got here...but no output.
      return encoded.join('');
  }
  
  function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    var inputFiles = evt.dataTransfer.files; // Gets a FileList object - a list of File objects.
    var fReader = new FileReader () ;  
    var output = [];
    for (var i = 0, f; f = inputFiles[i]; i++) {
      if ( !f.type.match('image.*')) { continue; }    //Breaks out of current loop iteration, i.e. skip non-image files
      alert ( "Image file found!");
      
      fReader.onLoad = (
        function (aFile) {
          return function (e) { output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
                  '<br><br>' , b64Enc(e.target.result) , '<br><br>End File', '</li>');
          };
        }
      ) (f);
      
      fReader.readAsText(f);
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
  }
  
  // Setup the dnd listeners.  [Slightly modified by JC]
  var dropZone = document.getElementById('filedrag');
  dropZone.addEventListener('dragover', handleDragOver, false);
  dropZone.addEventListener('drop', handleFileSelect, false);
</script>
And the original sample that I am using as a template:

Code:
//////////////////////
//  from http://www.html5rocks.com/en/tutorials/file/dndfiles/
//////////////////////
  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // Loop through the FileList and render image files as thumbnails.
    for (var i = 0, f; f = files[i]; i++) {

      // Only process image files.
      if (!f.type.match('image.*')) {
        continue;
      }

      var reader = new FileReader();

      // Closure to capture the file information.
      reader.onload = (
          function(theFile) {
              return function(e) {
                // Render thumbnail.
                var span = document.createElement('span');
                span.innerHTML = ['<img class="thumb" src="', e.target.result,
                                  '" title="', escape(theFile.name), '"/>'].join('');
                document.getElementById('list').insertBefore(span, null);
              };
          }
      )(f);

      // Read in the image file as a data URL.
      reader.readAsDataURL(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
it is that inner-inner function that uses the 'e.target.result' that is confusing me a good bit. I am not quite grasping what the flow of calls and parameters is, and what the truly represents.

Thanks again,
J
 
  • #6
33,733
5,422
OK, so here is my updated code, and down below that, the sample code that I am using as my template. There is a curious structure going on there that I don't quite get, and had disassembled slightly in my earlier version of code originally posted here. This new version of my code much more closely mimics the sample, but still doesn't work. Still kind of fumbling around in the dark; any suggestions appreciated.

My current code:

Code:
<script>
    //  From:  http://www.html5rocks.com/en/tutorials/file/dndfiles/
    //  JC update:  changing the handleFileSelect() function to do the base64 Processing
  
  function b64Enc(str) {
      {/*
       * base64.js - Base64 encoding and decoding functions
       * See: http://developer.mozilla.org/en/docs/DOM:window.btoa
       *      http://developer.mozilla.org/en/docs/DOM:window.atob
       * Copyright (c) 2007, David Lindquist <david.lindquist@gmail.com>
       * Released under the MIT license
       *
       * JC, update:  Removed the 'atob' section of code; only need ENcoding, not DEcoding.
       *     Removed 'btoa' naming wrapper-function; just expose the code directly in b64Enc()
       */}
      var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
      var encoded = [];
      var c = 0;
      while (c < str.length) {
          var b0 = str.charCodeAt(c++);
          var b1 = str.charCodeAt(c++);
          var b2 = str.charCodeAt(c++);
          var buf = (b0 << 16) + ((b1 || 0) << 8) + (b2 || 0);
          var i0 = (buf & (63 << 18)) >> 18;
          var i1 = (buf & (63 << 12)) >> 12;
          var i2 = isNaN(b1) ? 64 : (buf & (63 << 6)) >> 6;
          var i3 = isNaN(b2) ? 64 : (buf & 63);
          encoded[encoded.length] = chars.charAt(i0);
          encoded[encoded.length] = chars.charAt(i1);
          encoded[encoded.length] = chars.charAt(i2);
          encoded[encoded.length] = chars.charAt(i3);
      }
      alert ( "Done" ) ;   //OK, it got here...but no output.
      return encoded.join('');
  }
  
  function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    var inputFiles = evt.dataTransfer.files; // Gets a FileList object - a list of File objects.
    var fReader = new FileReader () ;  
    var output = [];
    for (var i = 0, f; f = inputFiles[i]; i++) {
      if ( !f.type.match('image.*')) { continue; }    //Breaks out of current loop iteration, i.e. skip non-image files
      alert ( "Image file found!");
      
      fReader.onLoad = (
        function (aFile) {
          return function (e) { output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
                  '<br><br>' , b64Enc(e.target.result) , '<br><br>End File', '</li>');
          };
        }
      ) (f);
      
      fReader.readAsText(f);
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
  }
  
  // Setup the dnd listeners.  [Slightly modified by JC]
  var dropZone = document.getElementById('filedrag');
  dropZone.addEventListener('dragover', handleDragOver, false);
  dropZone.addEventListener('drop', handleFileSelect, false);
</script>
And the original sample that I am using as a template:

Code:
//////////////////////
//  from http://www.html5rocks.com/en/tutorials/file/dndfiles/
//////////////////////
  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // Loop through the FileList and render image files as thumbnails.
    for (var i = 0, f; f = files[i]; i++) {

      // Only process image files.
      if (!f.type.match('image.*')) {
        continue;
      }

      var reader = new FileReader();

      // Closure to capture the file information.
      reader.onload = (
          function(theFile) {
              return function(e) {
                // Render thumbnail.
                var span = document.createElement('span');
                span.innerHTML = ['<img class="thumb" src="', e.target.result,
                                  '" title="', escape(theFile.name), '"/>'].join('');
                document.getElementById('list').insertBefore(span, null);
              };
          }
      )(f);

      // Read in the image file as a data URL.
      reader.readAsDataURL(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
it is that inner-inner function that uses the 'e.target.result' that is confusing me a good bit. I am not quite grasping what the flow of calls and parameters is, and what the truly represents.

Thanks again,
J
The statement immediately above sets a function that will be called when the 'change' event is raised on the element in your web page whose id is 'files'. The name of the function that will be called is handleFileSelect. Calling addEventListener tells the system that you want to listen for events of a specified type. The variable e represents the events.

When handleFileSelect actually gets called, it executes the statements listed in this function; namely, looping through each file in a list (an array) of files; opening a file reader for each; calling an anonymous function that has an embedded anonymous function.

That's how it looks to me. You can verify for yourself what's happening by using the debugging tools available in your browser by pressing F12. IE, Chrome, and Safari have the debugging tools. You'll probably need to manually set a breakpoint in your code by adding debugger; on the line where you want the debugger to stop.

Hope this helps.
 
  • #7
jjc
21
0
That's how it looks to me. You can verify for yourself what's happening by using the debugging tools available in your browser by pressing F12. IE, Chrome, and Safari have the debugging tools. You'll probably need to manually set a breakpoint in your code by adding debugger; on the line where you want the debugger to stop.

Hope this helps.
Mark,
Thanks for pointing out the debugger tools; I didn't know about those yet. That could help a lot.
I think that much of my confusion was trying to figure WHAT was being passed to all these different layers of functions (the two nested functions of the "onLoad" event function, and the new function that I put in), and what I needed to pass & return myself. The new function I put in ('b64Enc(str)') takes in a string, but I frankly wasn't 100% certain which of the file-reader types (I have tried both the Binary and the AsText ones, same results) I needed to use to pass the proper string-format to my new encoding code.
Or perhaps I just have the reading part in the wrong spot. It is my understanding that it is the reading part (i.e " fReader.readAsText(f); " ) that will trigger the "onLoad" event when the file being processed by the reader reaches the end. Do the HTML5 functions read IN a string of that type, or OUTPUT a string of the named type?

Thanks,
J
 
  • #9
jjc
21
0
Mark,
Thanks for the reply; I am still looking into this, but have put it on the back burner for a few days. I will re-read things and hope that I can glean what you mention.

-- J
 

Related Threads on HTML5 and Javascript file handling

Replies
3
Views
16K
  • Last Post
Replies
23
Views
2K
  • Last Post
Replies
5
Views
1K
  • Last Post
Replies
7
Views
2K
Replies
4
Views
616
  • Last Post
Replies
3
Views
3K
  • Last Post
Replies
4
Views
3K
  • Last Post
Replies
6
Views
2K
  • Last Post
Replies
4
Views
2K
  • Last Post
Replies
11
Views
2K
Top