0

Hope someone can help? I have read a number of SE posts on FileReader, Blobs, Base64 and EXIF and cobbled together something that is just about there. I am having problems understanding the structure of async functions and the order/delay in the process.

The script below loops through an uploaded array from a multi-part form. In the loop I have readAsDataURL to get Base64 String, and then attempting to readAsArrayBuffer to get the EXIF orientation code from the Blob before transforming a canvas.

I am clearly mixing up the async and getting a little lost.

At this stage it displays the images (and correctly orientates the first) but doesn't read the new EXIF orientation and all images are then rotated the same (presumably because the ArrayBuffer is not in sync?). If I upload more images at once there is some duplication which again must be a sync problem....

$(document).on('change', '#files', function(evt){
var files = evt.target.files;
var readerBase64;
var b64 = [];
var ab = [];
var c=0;
for (var i = 0; f = files[i]; i++) {
    console.log('LOOP');
    var blob = f;
    var readerBase64 = new FileReader(); 
    readerBase64.onload = function(evt){
        // console.log('BASE 64 ' + evt.target.result);
        b64.push(evt.target.result);
        var reader = new FileReader(); 
         reader.onloadend = (function(theFile) {
            console.log(this);
            console.log('READER');
             return function(e) {
                console.log('RETURN');
                ab.push(e.target.result);
                 console.log(ab[c]);

                 var view = new DataView(ab[c]);
                  ori = 1;
                    if (view.getUint16(0, false) != 0xFFD8){ori = -2};

                    var length = view.byteLength,
                        offset = 2;

                    while (offset < length) {
                      var marker = view.getUint16(offset, false);
                      offset += 2;

                      if (marker == 0xFFE1) {
                        if (view.getUint32(offset += 2, false) != 0x45786966) {
                          ori = -1;
                        }
                        var little = view.getUint16(offset += 6, false) == 0x4949;
                        offset += view.getUint32(offset + 4, little);
                        var tags = view.getUint16(offset, little);
                        offset += 2;

                        for (var i = 0; i < tags; i++)
                     if (view.getUint16(offset + (i * 12), little) == 0x0112)
                           ori = view.getUint16(offset + (i * 12) + 8, little);
                      }
                      else if ((marker & 0xFF00) != 0xFF00) break;
                      else offset += view.getUint16(offset, false);
                    }      
                 console.log('ori '+ori);
 //    console.log(b64[c]);

                    base64String = b64[c];
                    var img = document.createElement('img');

                    img.setAttribute('src',base64String);
                    //img.src = e.target.result;
                    img.onload = function () {
                    var canvas = document.createElement('canvas');

                    var MAX_WIDTH =1000;
                    var MAX_HEIGHT =800;
                    var width = img.width;
                    var height = img.height;

                    if (width > height) {
                      if (width > MAX_WIDTH) {
                        height *= MAX_WIDTH / width;
                        width = MAX_WIDTH;
                      }
                    } else {
                      if (height > MAX_HEIGHT) {
                        width *= MAX_HEIGHT / height;
                        height = MAX_HEIGHT;
                      }
                    }
                    canvas.width = width;
                    canvas.height = height;
                    var ctx = canvas.getContext("2d");


                    // set proper canvas dimensions before transform & export
                    if (4 < ori && ori < 9) {
                    canvas.width = height;
                    canvas.height = width;
                    } else {
                    canvas.width = width;
                      canvas.height = height;
                    }

                    // transform context before drawing image
                        switch (ori) {
                      case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
                      case 3: ctx.transform(-1, 0, 0, -1, width, height ); break;
                      case 4: ctx.transform(1, 0, 0, -1, 0, height ); break;
                      case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
                      case 6: ctx.transform(0, 1, -1, 0, height , 0); break;
                      case 7: ctx.transform(0, -1, -1, 0, height , width); break;
                      case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
                      default: break;
                    }                       

                    ctx.drawImage(img, 0, 0, width, height);

                    var dataurl = canvas.toDataURL("image/jpg");

                    //alert(dataurl);
                     // Render thumbnail.
                     var span = document.createElement('span');
                    //var ii = $('#list span').length - 2;
                    // if(ii > 0){
                    //  span.setAttribute('class','hide');
                    //  $('#list span:nth-child(3) p').text("+"+ii);
                    //  }

                      span.innerHTML = 
                      [
                        '<input type="image" class="thumb" name="imgs[]" value="', 
                        dataurl,
                        '" src="', dataurl, 
                        '"/>'
                      ].join('');

                      document.getElementById('list').insertBefore(span, null);                  

                      c++;  
                 }
             }
         })(f);         
         reader.readAsArrayBuffer(blob.slice(0, 64 * 1024));
    }
    readerBase64.readAsDataURL(f);
    //      console.log(f);
 }

}); // Files change

Ozbodd
  • 73
  • 8

1 Answers1

1

I have solved it, but perhaps not very gracefully. The uploaded, resized and orientation corrected images are added to the form in an element named #list ready for posting to the server. Here is the code for anyone else:

$(document).on('change', '#files', function(evt){
var files = evt.target.files;
var f;
for (var i = 0; f = files[i]; i++) {
    var blob = f;
    getOrientation(f, function(ori , f) {
        console.log(f);
        var readerBase64 = new FileReader(); 
        readerBase64.onload = function(evt){

            console.log(ori);
            console.log(evt.target.result);
            base64String = evt.target.result;
            var img = document.createElement('img');

            img.setAttribute('src',base64String);
            //img.src = e.target.result;
            img.onload = function () {
            var canvas = document.createElement('canvas');

            var MAX_WIDTH =1000;
            var MAX_HEIGHT =800;
            var width = img.width;
            var height = img.height;

            if (width > height) {
              if (width > MAX_WIDTH) {
                height *= MAX_WIDTH / width;
                width = MAX_WIDTH;
              }
            } else {
              if (height > MAX_HEIGHT) {
                width *= MAX_HEIGHT / height;
                height = MAX_HEIGHT;
              }
            }
            canvas.width = width;
            canvas.height = height;
            var ctx = canvas.getContext("2d");


            // set proper canvas dimensions before transform & export
            if (4 < ori && ori < 9) {
            canvas.width = height;
            canvas.height = width;
            } else {
            canvas.width = width;
              canvas.height = height;
            }

            // transform context before drawing image
                switch (ori) {
              case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
              case 3: ctx.transform(-1, 0, 0, -1, width, height ); break;
              case 4: ctx.transform(1, 0, 0, -1, 0, height ); break;
              case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
              case 6: ctx.transform(0, 1, -1, 0, height , 0); break;
              case 7: ctx.transform(0, -1, -1, 0, height , width); break;
              case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
              default: break;
            }                       

            ctx.drawImage(img, 0, 0, width, height);

            var dataurl = canvas.toDataURL("image/jpg");

            //alert(dataurl);
             // Render thumbnail.
             var span = document.createElement('span');
            //var ii = $('#list span').length - 2;
            // if(ii > 0){
            //  span.setAttribute('class','hide');
            //  $('#list span:nth-child(3) p').text("+"+ii);
            //  }

              span.innerHTML = 
              [
                '<input type="image" class="thumb" name="imgs[]" value="', 
                dataurl,
                '" src="', dataurl, 
                '"/>'
              ].join('');

              document.getElementById('list').insertBefore(span, null);             

            }

        };
        readerBase64.readAsDataURL(f);
    });
   }
});

Using the function I found on here with a minor change to send the file Blob back with the orientation in the callback:

 //from http://stackoverflow.com/a/32490603
function getOrientation(file, callback) {
  var reader = new FileReader();

  reader.onload = function(event) {
    var view = new DataView(event.target.result);

    if (view.getUint16(0, false) != 0xFFD8) return callback(-2, file);

    var length = view.byteLength,
        offset = 2;

    while (offset < length) {
      var marker = view.getUint16(offset, false);
      offset += 2;

      if (marker == 0xFFE1) {
        if (view.getUint32(offset += 2, false) != 0x45786966) {
          return callback(-1 , file);
        }
        var little = view.getUint16(offset += 6, false) == 0x4949;
        offset += view.getUint32(offset + 4, little);
        var tags = view.getUint16(offset, little);
        offset += 2;

        for (var i = 0; i < tags; i++)
          if (view.getUint16(offset + (i * 12), little) == 0x0112)
            return callback(view.getUint16(offset + (i * 12) + 8, little) , file);
      }
      else if ((marker & 0xFF00) != 0xFF00) break;
      else offset += view.getUint16(offset, false);
    }
    return callback(-1 , file);
  };

  reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
};

The solution for the upload was to intercept the post and use AJAX and JQuery to build the POST data from the input[type='image']

    $(document).on('submit',function(e){
    e.preventDefault();
    var data = $('#formFeed').serializeArray(); 
        var msg = $('textarea').val();
        data.push({name: 'msg', value: msg});
        var y;
        $('input[type="image"]').each(function(i, obj) {
            y = $(this).attr('src');
            data.push({name: 'img', value: y});
        });

        $.ajax({
            cache: false,
            type : 'POST',
            url  : 'http://localhost:3001/newFeed', 
            data: data,
            contentType: "application/x-www-form-urlencoded",
            success : function(data) {
                console.log(data);
            }
        }); 

});
Ozbodd
  • 73
  • 8