2

I'm trying to send created photo in android via ajax via remote API. I'm using Camera Picture Background plugin.

Photo is created properly, I'm getting it via ajax GET request and encode it to base64 format. In debugging tool I can see image itself through GET request log.

Next I parse it base64 to Blob and try to attach it to FormData:

var fd = new FormData();
fd.append('photo', blobObj);
$.ajax({
  type: 'POST',
  url: 'myUrl',
  data: fd,
  processData: false,
  contentType: false
}).done(function(resp){
  console.log(resp);
}). [...]

But when I send the FormData I see in debugger that FormData in request equals to: {photo: null}.

Btw. if I try to console.log my blobObj earlier, I see it is a blob, with its size, type properties and slice method - why it becomes a null after appending to FormData?

EDIT:

console.log(blobObj); gives:

Blob {type: "image/jpeg", size: 50778, slice: function}

EDIT2 - STEP BY STEP CODE:

I have url to local image, let's assume it is stored in imagePath variable.

First, I get this file and parse it to base64:

function base64Encode(){
  var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var out = "", i = 0, len = str.length, c1, c2, c3;

  while (i < len) {
    c1 = str.charCodeAt(i++) & 0xff;
    if (i == len) {
      out += CHARS.charAt(c1 >> 2);
      out += CHARS.charAt((c1 & 0x3) << 4);
      out += "==";
      break;
    }
    c2 = str.charCodeAt(i++);
    if (i == len) {
      out += CHARS.charAt(c1 >> 2);
      RS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
      out += CHARS.charAt((c2 & 0xF) << 2);
      out += "=";
      break;
    }
    c3 = str.charCodeAt(i++);
    out += CHARS.charAt(c1 >> 2);
    out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
    out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
    out += CHARS.charAt(c3 & 0x3F);
  }
  return out;
} 

function getFile(fileData){
  var dfd = new $.Deferred();
  $.ajax({
    type: 'GET',
    url: fileData,
    mimeType: "image/jpeg; charset=x-user-defined"
  }).done(function(resp){
    var file = base64Encode(resp);

    dfd.resolve(file);
  }).fail(function(err){
    console.warn('err', err);
    dfd.resolve();
  });

  return dfd.promise();
};

$.when(getFile(imagePath)).then(function(resp){
    var fd = new FormData();

    resp = 'data:image/jpeg;base64,' + resp;

    var imgBlob = new Blob([resp], {type : 'image/jpeg'});

    fd.append('photo', img, 'my_image.jpg');

    $.ajax({
      type: 'POST',
      url: 'myUrlToUploadFiles',
      data: fd,
      processData: false,
      contentType: false
    }).done(function(resp){
      console.log(resp);
    }). [...]
});
lukaszkups
  • 5,790
  • 9
  • 47
  • 85
  • 1
    Make blob like this `var blob = new Blob([payLoad], {type : 'image/png'});` – Vinay Dec 30 '16 at 16:05
  • I'm getting an error this time: `The file you uploaded was either not an image or a corrupted image` – lukaszkups Dec 30 '16 at 17:05
  • 1
    How have you converted this base64-image into a blob? I use the filereader and its readAsArrayBuffer method as first param for the blob-constructor. And using a filename might help as well like [here](http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name) – Blauharley Dec 30 '16 at 17:38
  • I've updated my question with details. I tried to use `FileReader` but when i was sending file it was also sent as FileData that equals in the end `{photo: null}` - so I've tried to solve this with code above. – lukaszkups Dec 30 '16 at 18:01
  • 1
    Ok, sorry I used filereader for url-based images, for base64 strings atob should be used instead, it takes a base64 string and converts it directly to a blob. [reference](http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript). And If I were you I would use [readAsDataURL](http://stackoverflow.com/questions/17710147/image-convert-to-base64) of filereader or [todataurl](http://stackoverflow.com/questions/6150289/how-to-convert-image-into-base64-string-using-javascript) of canvas instead to convert an image into a base64 image. – Blauharley Dec 30 '16 at 18:16
  • But I don't want to use canvas - I would like to use only url to image which I don't know its size/dimenstions (width/height) (soultion in single function) - or should I anyway print on canvas given image every time? – lukaszkups Dec 30 '16 at 18:32
  • 1
    Then I would use filereader and its readasdataurl method, in this way you do not need a ajax call and knowledge about its dimensions. this base64, coming out as a result of onload, you can convert into a blob by using atob. – Blauharley Dec 30 '16 at 18:54

1 Answers1

1

I've not done this recently, but this works with me. I hope it also works with you:

function getBase64ImageByURL(url) {
  var dfd = new $.Deferred();
  var xhr = new XMLHttpRequest();
  xhr.responseType = 'blob';
  xhr.onload = function() {
    var reader = new FileReader();
    reader.onloadend = function() {
      dfd.resolve(reader.result);
    }
    reader.readAsDataURL(xhr.response);
  };
  xhr.open('GET', url);
  xhr.send();
  return dfd.promise();
}

function base64ToBlob(base64Image,toMimeType) {
  var byteCharacters = atob(base64Image.replace('data:'+toMimeType+';base64,',''));
  var byteNumbers = new Array(byteCharacters.length);
  for (var i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
  }
  var byteArray = new Uint8Array(byteNumbers);
  var blob = new Blob([byteArray], {
    type: toMimeType
  });
  return blob;
}



var imageUrl = "https://upload.wikimedia.org/wikipedia/commons/4/49/Koala_climbing_tree.jpg";

getBase64ImageByURL(imageUrl).then(function(base64Image){
  var blob = base64ToBlob(base64Image,'image/jpeg');
  var fd = new FormData();
  fd.append('file', blob, 'my_image.jpg');
  $.ajax({
    url: 'http://your_host/uploads/testupload.php',
    data: fd,
    type: 'POST',
    contentType: false,
    processData: false,
    success:function(res){
      console.log(res);
    },
    error:function(err){
      console.log(err);
    }
  })
});

On server-side(testupload.php):

<?php

    if ( 0 < $_FILES['file']['error'] ) {
        echo 'Error: ' . $_FILES['file']['error'] . '<br>';
    }
    else {
        $result = move_uploaded_file($_FILES['file']['tmp_name'], $_SERVER["DOCUMENT_ROOT"].$_SERVER["BASE"].'/uploads/'.'my_image.jpg');
        var_dump("image uploaded: ".$result);
    }

?>

It might be necessary to modify some read/write-permissions on a directory before move_uploaded_file succeeds in moving the uploaded image to this directory.

The function getBase64ImageByURL could already return a blob-object but by returning a base64-image you can show an user this image in a html-image-tag before uploading it for instance.


If there is no need to show an user that image, then you can also shorten all steps:

function getBlobImageByURL(url) {
  var dfd = new $.Deferred();
  var xhr = new XMLHttpRequest();
  xhr.responseType = 'blob';
  xhr.onload = function() {
    dfd.resolve(xhr.response);
  };
  xhr.open('GET', url);
  xhr.send();
  return dfd.promise();
}

getBlobImageByURL(imageUrl).then(function(imageBlob){
  var fd = new FormData();
  fd.append('file', imageBlob, 'my_image.jpg');
  console.log(fd.get('file'));// File-object 
  $.ajax({
    url: 'http://your_host/uploads/testupload.php',
    data: fd,
    type: 'POST',
    contentType: false,
    processData: false,
    success:function(res){
      console.log(res);
    },
    error:function(err){
      console.log(err);
    }
  })
});

references to both modified functions base64ToBlob and getBase64ImageByURL

Community
  • 1
  • 1
Blauharley
  • 4,186
  • 6
  • 28
  • 47
  • hello, I've changed code to use canvas, but still, when sending, formData `file` (in my case - `photo`) is still `null`! :/ btw. I don't have access to server code. – lukaszkups Jan 02 '17 at 10:55
  • With canvas you can crop the image before uploading but when you need the whole image I prefer filereader or the XMLHttpRequest object. I attached a shorter variante that just uses a XMLHttpRequest object. I tested this with chrome/FF and edge. Update your code, please. – Blauharley Jan 02 '17 at 11:54
  • Finally made it! Server-side code had an error! Thank You all for Your efforts - it was working since the beginning ;) – lukaszkups Jan 02 '17 at 12:19