272

I'm using Nihilogic's "Canvas2Image" JavaScript tool to convert canvas drawings to PNG images. What I need now is to turn those base64 strings that this tool generates, into actual PNG files on the server, using PHP.

In short, what I'm currently doing is to generate a file on the client side using Canvas2Image, then retrieve the base64-encoded data and send it to the server using AJAX:

// Generate the image file
var image = Canvas2Image.saveAsPNG(canvas, true);   

image.id = "canvasimage";
canvas.parentNode.replaceChild(image, canvas);

var url = 'hidden.php',
data = $('#canvasimage').attr('src');

$.ajax({ 
    type: "POST", 
    url: url,
    dataType: 'text',
    data: {
        base64data : data
    }
});

At this point, "hidden.php" receives a data block that looks like data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABE...

From this point on, I'm pretty much stumped. From what I've read, I believe that I'm supposed to use PHP's imagecreatefromstring function, but I'm not sure how to actually create an actual PNG image from the base64-encoded string and store it on my server. Please aid!

miken32
  • 42,008
  • 16
  • 111
  • 154
Andrei Oniga
  • 8,219
  • 15
  • 52
  • 89
  • you need to parse it. you can extract image type from there and then use base64_decode and save that string in a file by your image type – Constantin Jul 16 '12 at 19:51
  • @Constantine Can you be more specific, please? – Andrei Oniga Jul 16 '12 at 19:53
  • 7
    $data = $_REQUEST['base64data']; $image = explode('base64,',$data); file_put_contents('img.png', base64_decode($image[1])); – Constantin Jul 16 '12 at 19:55
  • you can post the full code , from the snapshot and till you send the data, its not working for me. – Ofir Attia Apr 11 '13 at 11:30
  • Try this, worked for me. $base64string = ''; $uploadpath = 'YOUR UPLOAD DIR PATH'; $parts = explode(";base64,", $base64string); $imagebase64 = base64_decode($parts[1]); $file = $uploadpath . uniqid() . '.png'; file_put_contents($file, $imagebase64); – Piyush Sapariya Nov 28 '22 at 10:53

17 Answers17

541

You need to extract the base64 image data from that string, decode it and then you can save it to disk, you don't need GD since it already is a png.

$data = 'data:image/png;base64,AAAFBfj42Pj4';

list($type, $data) = explode(';', $data);
list(, $data)      = explode(',', $data);
$data = base64_decode($data);

file_put_contents('/tmp/image.png', $data);

And as a one-liner:

$data = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $data));

An efficient method for extracting, decoding, and checking for errors is:

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

    if (!in_array($type, [ 'jpg', 'jpeg', 'gif', 'png' ])) {
        throw new \Exception('invalid image type');
    }
    $data = str_replace( ' ', '+', $data );
    $data = base64_decode($data);

    if ($data === false) {
        throw new \Exception('base64_decode failed');
    }
} else {
    throw new \Exception('did not match data URI with image data');
}

file_put_contents("img.{$type}", $data);
Community
  • 1
  • 1
drew010
  • 68,777
  • 11
  • 134
  • 162
  • You have $type in your example. What should that value be? – Jon Feb 11 '17 at 20:10
  • 1
    @Jon `$type` gets assigned a value from the explode, depending on whether it's data:image/jpg or data:image/png etc. – drew010 Feb 16 '17 at 16:17
  • i got this error: malformed utf-8 characters possibly incorrectly encoded what should i do? – aldo Jul 02 '17 at 09:02
  • 1
    @aldo Probably post a new question with code and detail. Pretty impossible to say based on just that message and without seeing your code or knowing how the image is encoded and how it's sent to the script. – drew010 Jul 05 '17 at 15:46
  • I guess `$type` will not work with `.svg` file, mimetype: `image/svg+xml` – Nuryagdy Mustapayev Nov 23 '20 at 08:57
  • @drew010 in the last part ``file_put_contents``do you know how to save on an FTP server without saving it locally? – MamaWin Aug 03 '22 at 02:26
143

Try this:

file_put_contents('img.png', base64_decode($base64string));

file_put_contents docs

Some Guy
  • 15,854
  • 10
  • 58
  • 67
  • 8
    I tried this but it's including the `data:image/png;base64,` which breaks the file. – Michael J. Calkins Sep 03 '13 at 19:13
  • 3
    @MichaelCalkins Breaks it for me, too. drew010's answer seems the only solution that works consistently. – David John Welsh Nov 24 '13 at 03:21
  • 34
    If you're including the `data:image/png;base64,` then the string you're passing to `base64_decode` isn't valid base64. You need to only send the data, which is what drew010's answer is illustrating. There is nothing broken with this answer. You can't copy and paste without understanding and expect it to "just work". – Cypher May 07 '14 at 20:51
  • 5
    Not working for the example provided in the question, which is not a base64 content, and first has to be split into pieces (see the accepted answer). – Benjamin Piette May 26 '15 at 12:38
  • my image has been successfully saved. but when i write url of the image I see a blank image. I am sure that my dataURL is correct, because I tested that using window.open(dataURL). Why a blank image? – partho Aug 16 '15 at 05:02
  • If you generate the image with this method it won't even be able to be previewed on Mac. Not sure about Windows. Accepted answer one liner worked like a charm. – Alvaro May 12 '20 at 12:04
  • this method need one image at that place every time. May be overwrite every time – Piyush Sapariya Nov 28 '22 at 10:52
56

I had to replace spaces with plus symbols str_replace(' ', '+', $img); to get this working.

Here is the full code

$img = $_POST['img']; // Your data 'data:image/png;base64,AAAFBfj42Pj4';
$img = str_replace('data:image/png;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$data = base64_decode($img);
file_put_contents('/tmp/image.png', $data);

Hope that helps.

Ben
  • 1,989
  • 22
  • 25
  • 4
    Your line `$img = str_replace(' ', '+', $img);` helped me much, thanks! Why do I have to convert the space to "+" before? – Sven Aug 16 '18 at 10:07
  • 1
    Is there a need to validate this data in some way? like you normally would with $_FILES data? and if so how is that possible – ii iml0sto1 Sep 19 '20 at 13:33
25

It worth to say that discussed topic is documented in RFC 2397 - The "data" URL scheme (https://www.rfc-editor.org/rfc/rfc2397)

Because of this PHP has a native way to handle such data - "data: stream wrapper" (http://php.net/manual/en/wrappers.data.php)

So you can easily manipulate your data with PHP streams:

$data = 'data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7';

$source = fopen($data, 'r');
$destination = fopen('image.gif', 'w');

stream_copy_to_stream($source, $destination);

fclose($source);
fclose($destination);
Community
  • 1
  • 1
Vladimir Posvistelik
  • 3,843
  • 24
  • 28
  • 3
    I like this answer, it should have more votes, it's using native functions, no dirty homemade data manipulation ! Although, any idea about performances ? Is it, as one could expect, faster than `preg_replace` + `file_put_contents` ? – Bonswouar Oct 18 '18 at 10:58
  • 2
    @Bonswouar Thanks. Regarding your question: in my opinion the best way to ensure that your application haven't got performance issues would be to measure its performance in your particular case (based on environment, expected and allowed file size and so on). We could try to play with code here and show numbers in the answer, but the truth is that for your exact case it could bring more harm than positive outcome. It's always better to make sure on your own (especially if we're talking performance-critical parts of the app). – Vladimir Posvistelik Apr 08 '19 at 09:13
14

Taken the @dre010 idea, I have extended it to another function that works with any image type: PNG, JPG, JPEG or GIF and gives a unique name to the filename

The function separate image data and image type

function base64ToImage($imageData){
    $data = 'data:image/png;base64,AAAFBfj42Pj4';
    list($type, $imageData) = explode(';', $imageData);
    list(,$extension) = explode('/',$type);
    list(,$imageData)      = explode(',', $imageData);
    $fileName = uniqid().'.'.$extension;
    $imageData = base64_decode($imageData);
    file_put_contents($fileName, $imageData);
}
PolloZen
  • 654
  • 6
  • 12
11

Well your solution above depends on the image being a jpeg file. For a general solution i used

$img = $_POST['image'];
$img = substr(explode(";",$img)[1], 7);
file_put_contents('img.png', base64_decode($img));
devil_io
  • 191
  • 2
  • 5
7

Total concerns:

$data = 'data:image/png;base64,AAAFBfj42Pj4';

// Extract base64 file for standard data
$fileBin = file_get_contents($data);
$mimeType = mime_content_type($data);

// Check allowed mime type
if ('image/png'==$mimeType) {
    file_put_contents('name.png', $fileBin);
}

http://php.net/manual/en/wrappers.data.php

http://php.net/manual/en/function.mime-content-type.php

Greg Schmidt
  • 5,010
  • 2
  • 14
  • 35
Nick Tsai
  • 3,799
  • 33
  • 36
7

PHP has already a fair treatment base64 -> file transform

I use to get it done coding this way:

$blob=$_POST['blob']; // base64 coming from an url, for example

//Now, let's save the image file:

file_put_contents('myfile.png',file_get_contents($blob));
PYK
  • 3,674
  • 29
  • 17
  • This should be the accepted answer since the OP have the base64 string formatted as an URL, beginning with `data:image/png;base64,`. – Puspam Feb 07 '22 at 14:16
6

One-linear solution.

$base64string = 'data:image/png;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7';
file_put_contents('img.png', base64_decode(explode(',',$base64string)[1]));
Piotr
  • 1,777
  • 17
  • 24
6

This code works for me check below code:

<?php
define('UPLOAD_DIR', 'images/');
$image_parts = explode(";base64,", $_POST['image']);
$image_type_aux = explode("image/", $image_parts[0]);
$image_type = $image_type_aux[1];
$image_base64 = base64_decode($image_parts[1]);
$file = UPLOAD_DIR . uniqid() . '.png';
file_put_contents($file, $image_base64);
?>
Gauravbhai Daxini
  • 2,032
  • 2
  • 22
  • 28
5

based on drew010 example I made a working example for easy understanding.

imagesaver("data:image/jpeg;base64,/9j/4AAQSkZJ"); //use full base64 data 

function imagesaver($image_data){

    list($type, $data) = explode(';', $image_data); // exploding data for later checking and validating 

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

        if (!in_array($type, [ 'jpg', 'jpeg', 'gif', 'png' ])) {
            throw new \Exception('invalid image type');
        }

        $data = base64_decode($data);

        if ($data === false) {
            throw new \Exception('base64_decode failed');
        }
    } else {
        throw new \Exception('did not match data URI with image data');
    }

    $fullname = time().$type;

    if(file_put_contents($fullname, $data)){
        $result = $fullname;
    }else{
        $result =  "error";
    }
    /* it will return image name if image is saved successfully 
    or it will return error on failing to save image. */
    return $result; 
}
sanjeet bisht
  • 351
  • 3
  • 9
4

try this...

$file = $_POST['file']; //your data in base64 'data:image/png....';
$img = str_replace('data:image/png;base64,', '', $file);
file_put_contents('img/imag.png', base64_decode($img));
Paline
  • 51
  • 1
1

Assuming you have filename in $filename and your base64encoded string in $testfile my oneliner:

file_put_contents($filename,base64_decode(explode(',', $testfile)[1]))
hisablik
  • 111
  • 2
0

This function should work. this has the photo parameter that holds the base64 string and also path to an existing image directory should you already have an existing image you want to unlink while you save the new one.

 public function convertBase64ToImage($photo = null, $path = null) {
    if (!empty($photo)) {
        $photo = str_replace('data:image/png;base64,', '', $photo);
        $photo = str_replace(' ', '+', $photo);
        $photo = str_replace('data:image/jpeg;base64,', '', $photo);
        $photo = str_replace('data:image/gif;base64,', '', $photo);
        $entry = base64_decode($photo);
        $image = imagecreatefromstring($entry);

        $fileName = time() . ".jpeg";
        $directory = "uploads/customer/" . $fileName;

        header('Content-type:image/jpeg');

        if (!empty($path)) {
            if (file_exists($path)) {
                unlink($path);
            }
        }

        $saveImage = imagejpeg($image, $directory);

        imagedestroy($image);

        if ($saveImage) {
            return $fileName;
        } else {
            return false; // image not saved
        }
    }
}
Saviour Dela
  • 139
  • 1
  • 7
0

It's simple :

Let's imagine that you are trying to upload a file within js framework, ajax request or mobile application (Client side)

  1. Firstly you send a data attribute that contains a base64 encoded string.
  2. In the server side you have to decode it and save it in a local project folder.

Here how to do it using PHP

<?php 

$base64String = "kfezyufgzefhzefjizjfzfzefzefhuze"; // I put a static base64 string, you can implement you special code to retrieve the data received via the request.

$filePath = "/MyProject/public/uploads/img/test.png";

file_put_contents($filePath, base64_decode($base64String));

?>
Mustapha GHLISSI
  • 1,485
  • 1
  • 15
  • 16
0

If you want to randomly rename images, and store both the image path on database as blob and the image itself on folders this solution will help you. Your website users can store as many images as they want while the images will be randomly renamed for security purposes.

Php code

Generate random varchars to use as image name.

function genhash($strlen) {
        $h_len = $len;
        $cstrong = TRUE;
        $sslkey = openssl_random_pseudo_bytes($h_len, $cstrong);
        return bin2hex($sslkey);
}
$randName = genhash(3); 
#You can increase or decrease length of the image name (1, 2, 3 or more).

Get image data extension and base_64 part (part after data:image/png;base64,) from image .

$pos  = strpos($base64_img, ';');
$imgExten = explode('/', substr($base64_img, 0, $pos))[1];
$extens = ['jpg', 'jpe', 'jpeg', 'jfif', 'png', 'bmp', 'dib', 'gif' ];

if(in_array($imgExten, $extens)) {

   $imgNewName = $randName. '.' . $imgExten;
   $filepath = "resources/images/govdoc/".$imgNewName;
   $fileP = fopen($filepath, 'wb');
   $imgCont = explode(',', $base64_img);
   fwrite($fileP, base64_decode($imgCont[1]));
   fclose($fileP);

}

# => $filepath <= This path will be stored as blob type in database.
# base64_decoded images will be written in folder too.

# Please don't forget to up vote if you like my solution. :)
Community
  • 1
  • 1
0

I hope this will help you. I solved this issue with core php method. My solved code as below.

$base64string = 'BASE64 STRING GOES HERE';
$uploadpath   = 'YOUR UPLOAD DIR PATH';
$parts        = explode(";base64,", $base64string); //THIS WILL GET THE ORIGINAL FILE ENCODE STRING
$imagebase64  = base64_decode($parts[1]); //THIS WILL GET THE DECODED IMAGE STRING
$file         = $uploadpath . uniqid() . '.png'; // THIS WILL GIVE THE FILE NAME AND SET THE FILE PATH
file_put_contents($file, $imagebase64); // THIS FUNCTION WILL STORE THE IMAGE TO GIVEN PATH WITH FILE_NAME
Piyush Sapariya
  • 408
  • 3
  • 11