1
votes

I'm using axios to send a base64 string via ajax. Using the way below, I lose a lot of data somehow when it's encoded from base64 data back to a jpg. How can I send this without the data loss?

I grab the file from an input and send it to

var reader = new FileReader();
reader.readAsDataURL(file);

and that base64 string is sent as ajax with axios as

axios.post('url', {main: img})

A php script receives the post as:

$incoming = json_decode(file_get_contents('php://input'))->main;
$mainImage = str_replace('data:image/jpeg;base64,', '', $incoming);
$img = imagecreatefromstring(base64_decode($mainImage));
$imageSave = imagejpeg($img, './uploaded.jpg');

A recent file, for example, saved on the server is only 14k, but the original file I uploaded to the input field was 19k. I'm outputting the uploaded base64 on the client side to a preview div, and that image saves as a 19k jpg, so I assume it's the php script. Any ideas on what's causing the data loss? Maybe some axios config value?

2
Just save the results of base64_decode($mainImage) in a file directly, like: file_put_contents('./uploaded.jpg', base64_decode($mainImage));. Currently, you're creating a new Jpeg image, which will be slightly more compressed when you save it. - M. Eriksson
@MagnusEriksson I have to keep it on the server as a jpg. I guess that axios is sending my string of base64 data, but I don't know. My goal is to just get a jpg on the server using ajax, so any way that wouldn't lose data would be nice. - Harold
Try the above solution. What happens is, the front end is sending the binary image data base64 encoded. If you just decode it and save it to a file (with .jpg-extension, like my example), you will get an exact copy of the uploaded image. - M. Eriksson
@MagnusEriksson yo that worked! Gosh, I went through so many kinds of encoding and decoding to get this to even upload I guess I missed a more straightforward solution. If you like, please paste below so I can mark as the answer. Thanks a lot. - Harold

2 Answers

2
votes

What happens is, the front end is sending the binary image data base64 encoded.

Currently, you're decoding the image, creating a new image and saving it as jpg. That will just compress the image again.

If you just decode the data and save it to a file (with .jpg-extension), you will get an exact copy of the uploaded image.

incoming = json_decode(file_get_contents('php://input'))->main;
$mainImage = str_replace('data:image/jpeg;base64,', '', $incoming);
file_put_contents('./uploaded.jpg', base64_decode($mainImage));
0
votes

You don't need to use imagecreatefromstring.

JS

$.ajax({
    url: 'URL',
    type: "POST",
    processData: false,
    contentType: 'application/octet-stream',
    timeout: 120*1000,
    crossDomain: true,
    xhrFields: {withCredentials: true},
    data: base64Img,
    success: function (d) {
        console.log('Done!');
    }
})

PHP

$img = file_get_contents('php://input');

if (preg_match('/^data:image\/(\w+);base64,/', $img, $fileExt)) {
    $img = substr($img, strpos($img, ',') + 1);
    $fileExt = strtolower($fileExt[1]); // jpg, png, gif

    if (!in_array($fileExt, [ 'jpg', 'jpeg', 'gif', 'png' ])) {
        throw new \Exception('invalid image type');
    }
    if ($img === false) {
        throw new \Exception('base64_decode failed');
    }
} else {
    throw new \Exception('did not match data URI with image data');
}
file_put_contents( 'filename.'.$fileExt, base64_decode($img) );