简体   繁体   中英

Sending image manipulated via JS in AJAX POST request

I'm a server-side dev learning the ropes of vanilla JS. I need to clear my concepts regarding sending an Ajax POST request for an image object I'm creating in JS - this question is about that.

Imagine a web app where users upload photos for others to see. At the point of each image's upload, I use vanilla JS to confirm the image's mime-type (via interpreting magic numbers ), and then resize the image for optimization purposes.

After resizing, I do:

    var canvas = document.createElement('canvas');
    canvas.width = resized_width;
    canvas.height = resized_height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(source_img, 0, 0, resized_width, resized_height);
    var resized_img = new Image();
    resized_img.src = canvas.toDataURL("image/jpeg",0.7);
    return resized_img;

The image object returned has to be sent to the backend via an Ajax request. Something like:

function overwrite_default_submit(e) {
  e.preventDefault();
  var form = new FormData();
  form.append("myfile", resized_img, img_name);
  var xhr = new XMLHttpRequest();
  xhr.open('POST', e.target.action);
//  xhr.send(form); // uncomment to really send the request
}

However, the image object returned after resizing is essentially an HTML element like so <img src="data:image/jpeg;base64> . Whereas the object expected in the FormData object ought to be a File object, eg something like: File { name: "example.jpg", lastModified: 1500117303000, lastModifiedDate: Date 2017-07-15T11:15:03.000Z, webkitRelativePath: "", size: 115711, type: "image/jpeg" } .

So what do I do to fix this issue? Would prefer to learn the most efficient way of doing things here.

Btw, I've seen an example on SO of using the JS FILE object, but I'd prefer a more cross-browser method, given File garnered support from Safari, Opera Mobile and built-in Android browsers relatively recently .

Moreover, only want pure JS solutions since I'm using this as an exercise to learn the ropes. JQuery is on my radar, but for later.


The rest of my code is as follows (only included JPEG processing for brevity):

var max_img_width = 400;
var wranges = [max_img_width, Math.round(0.8*max_img_width), Math.round(0.6*max_img_width),Math.round(0.4*max_img_width),Math.round(0.2*max_img_width)];

// grab the file from the input and process it
function process_user_file(e) {
  var file = e.target.files[0];
  var reader = new FileReader();
  reader.onload = process_image;
  reader.readAsArrayBuffer(file.slice(0,25));
}

// checking file type programmatically (via magic numbers), getting dimensions and returning a compressed image
function process_image(e) {
  var img_width;
  var img_height;
  var view = new Uint8Array(e.target.result);
  var arr = view.subarray(0, 4);
  var header = "";
  for(var i = 0; i < arr.length; i++) {
     header += arr[i].toString(16);
  }
  switch (header) {
    case "ffd8ffe0":
    case "ffd8ffe1":
    case "ffd8ffe2":
    case "ffd8ffe3":
    case "ffd8ffe8":
        // magic numbers represent type = "image/jpeg";
        // use the 'slow' method to get the dimensions of the media
        img_file = browse_image_btn.files[0];
        var fr = new FileReader();
        fr.onload = function(){
          var dataURL = fr.result;
          var img = new Image();
          img.onload = function() {
              img_width = this.width;
              img_height = this.height;
              resized_img = resize_and_compress(this, img_width, img_height, 80);
            }
          img.src = dataURL;
        };
        fr.readAsDataURL(img_file);
        to_send = browse_image_btn.files[0];
        load_rest = true;
        subform.disabled = false;
        break;
    default:
        // type = "unknown"; // Or one can use the blob.type as fallback
        load_rest = false;
        subform.disabled = true;
        browse_image_btn.value = "";
        to_send = null;
        break;
    }
}

// resizing (& compressing) image
function resize_and_compress(source_img, img_width, img_height, quality){
    var new_width;
    switch (true) {
      case img_width < wranges[4]:
         new_width = wranges[4];
         break;
      case img_width < wranges[3]:
         new_width = wranges[4];
         break;
      case img_width < wranges[2]:
         new_width = wranges[3];
         break;
      case img_width < wranges[1]:
         new_width = wranges[2];
         break;
      case img_width < wranges[0]:
         new_width = wranges[1];
         break;
      default:
         new_width = wranges[0];
         break;
    }
    var wpercent = (new_width/img_width);
    var new_height = Math.round(img_height*wpercent);

    var canvas = document.createElement('canvas');
    canvas.width = new_width;
    canvas.height = new_height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(source_img, 0, 0, new_width, new_height);
    console.log(ctx);
    var resized_img = new Image();
    resized_img.src = canvas.toDataURL("image/jpeg",quality/100);
    return resized_img;
}

Update: I'm employing the following:

// converting image data uri to a blob object
function dataURItoBlob(dataURI,mime_type) {
  var byteString = atob(dataURI.split(',')[1]);
  var ab = new ArrayBuffer(byteString.length);
  var ia = new Uint8Array(ab);
  for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); }
  return new Blob([ab], { type: mime_type });
}

Where the dataURI parameter is canvas.toDataURL(mime_type,quality/100)

See to this SO post: How to get base64 encoded data from html image

I think you need to call 'canvas.toDataURL()' to get the actual base64 stream of the image.

var image = canvas.toDataURL();

Then upload it with a Form: Upload a base64 encoded image using FormData?

var data = new FormData();
data.append("image_data", image);

Untested, but this should be about it.

You should call the canvas.toBlob() to get the binary instead of using a base64 string.

it's async so you would have to add a callback to it.

canvas.toBlob(function(blob) {
  resized_img.onload = function() {
    // no longer need to read the blob so it's revoked
    URL.revokeObjectURL(this.url);
  };

  // Preview the image using createObjectURL
  resized_img.src = URL.createObjectURL(blob);

  // Attach the blob to the FormData
  var form = new FormData();
  form.append("myfile", blob, img_name);
}, "image/jpeg", 0.7);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM