153

I am trying to convert my base64 image string to an image file. This is my Base64 string:

http://pastebin.com/ENkTrGNG

Using following code to convert it into an image file:

function base64_to_jpeg( $base64_string, $output_file ) {
    $ifp = fopen( $output_file, "wb" ); 
    fwrite( $ifp, base64_decode( $base64_string) ); 
    fclose( $ifp ); 
    return( $output_file ); 
}

$image = base64_to_jpeg( $my_base64_string, 'tmp.jpg' );

But I am getting an error of invalid image, whats wrong here?

JasonMArcher
  • 12,386
  • 20
  • 54
  • 51
Badal
  • 3,287
  • 4
  • 29
  • 58

8 Answers8

279

The problem is that data:image/png;base64, is included in the encoded contents. This will result in invalid image data when the base64 function decodes it. Remove that data in the function before decoding the string, like so.

function base64_to_jpeg($base64_string, $output_file) {
    // open the output file for writing
    $ifp = fopen( $output_file, 'wb' ); 

    // split the string on commas
    // $data[ 0 ] == "data:image/png;base64"
    // $data[ 1 ] == <actual base64 string>
    $data = explode( ',', $base64_string );

    // we could add validation here with ensuring count( $data ) > 1
    fwrite( $ifp, base64_decode( $data[ 1 ] ) );

    // clean up the file resource
    fclose( $ifp ); 

    return $output_file; 
}
Austin Brunkhorst
  • 18,784
  • 5
  • 40
  • 56
  • 31
    i have very smart solution $filename_path = md5(time().uniqid()).".jpg"; $decoded=base64_decode($base64_string_img); file_put_contents("uploads/".$filename_path,$decoded); – Rizwan Gill Feb 06 '15 at 12:09
  • I had just raw base64 data without any prefix or so. therefor I had to change $data[1] to $data[0]. – rcpfuchs Apr 27 '16 at 06:05
  • 1
    @rcpfuchs if you have just raw base64, then what is the need to use $data,use it direct as written in asked question – Anant Feb 02 '17 at 16:35
  • If you are expecting either, you can use array_pop(). – HaLeiVi Jan 23 '19 at 05:48
  • @Trinh's answer is better for non-raw base64 strings:`file_put_contents($output_file, file_get_contents($base64_string));` – Miro May 06 '19 at 01:34
  • You can improve this function with check if $data has really more elements. – Jaroslav Štreit Aug 28 '19 at 23:25
  • @JaroslavŠtreit validation is noted in the comments – Austin Brunkhorst Aug 29 '19 at 17:56
  • 1
    bro you save my life! – Jacky Chong Sep 12 '19 at 02:55
  • I don't think this is an efficient way to go about it. Using explode on a large file say 2MB will substantially take a lot of computation time. – Nafiu Lawal Feb 12 '21 at 12:22
  • @NafiuLawal this is `O(n)`. Feel free to do some benchmarks, but I don't think there's much optimization that can be done without going closer to the metal. – Austin Brunkhorst Feb 12 '21 at 17:57
  • @AustinBrunkhorst you are right especially in php – Nafiu Lawal Feb 12 '21 at 21:01
  • @AustinBrunkhorst Not much optimisation you can do?? I hope you are joking! Your code goes through the ENTIRE string. Why not do explode(',', $base_string, 1) to stop after finding the comma?? Reading the first 20-ish chars from a string vs (potentially) several MBs? In any case, file_get_contents($base64_string) is waaaay more efficient. – Daniele Testa May 29 '21 at 10:29
49

An easy way I'm using:

file_put_contents($output_file, file_get_contents($base64_string));

This works well because file_get_contents can read data from a URI, including a data:// URI.

miken32
  • 35,483
  • 13
  • 81
  • 108
Henry Trần
  • 748
  • 6
  • 10
  • ingenious - thanks for answering. just remember you'll actually NEED data:image/png;base64, at the beginning, or else file_get_contents() will fail. – iateadonut Mar 30 '19 at 09:00
  • This solution is better than the accepted answer. – Barun Jun 14 '20 at 14:46
  • Yes, better & simpler solution. You can make file extension (if needed) using the identifier string `data:image/png;base64`. – Code Fithriya Oct 13 '20 at 09:22
48

You need to remove the part that says data:image/png;base64, at the beginning of the image data. The actual base64 data comes after that.

Just strip everything up to and including base64, (before calling base64_decode() on the data) and you'll be fine.

ashleedawg
  • 17,207
  • 5
  • 53
  • 80
aaaaaa123456789
  • 4,586
  • 18
  • 30
17

maybe like this

function save_base64_image($base64_image_string, $output_file_without_extension, $path_with_end_slash="" ) {
    //usage:  if( substr( $img_src, 0, 5 ) === "data:" ) {  $filename=save_base64_image($base64_image_string, $output_file_without_extentnion, getcwd() . "/application/assets/pins/$user_id/"); }      
    //
    //data is like:    data:image/png;base64,asdfasdfasdf
    $splited = explode(',', substr( $base64_image_string , 5 ) , 2);
    $mime=$splited[0];
    $data=$splited[1];

    $mime_split_without_base64=explode(';', $mime,2);
    $mime_split=explode('/', $mime_split_without_base64[0],2);
    if(count($mime_split)==2)
    {
        $extension=$mime_split[1];
        if($extension=='jpeg')$extension='jpg';
        //if($extension=='javascript')$extension='js';
        //if($extension=='text')$extension='txt';
        $output_file_with_extension=$output_file_without_extension.'.'.$extension;
    }
    file_put_contents( $path_with_end_slash . $output_file_with_extension, base64_decode($data) );
    return $output_file_with_extension;
}
NazS2
  • 29
  • 7
Shimon Doodkin
  • 3,676
  • 29
  • 33
  • 2
    This worked as a charm! Thank you a lot! Exactly what I was looking for. Just changed a little bit to match my specific requirements and got everything to work. – Sharpey Dec 20 '18 at 15:41
6

That's an old thread, but in case you want to upload the image having same extension-

    $image = $request->image;
    $imageInfo = explode(";base64,", $image);
    $imgExt = str_replace('data:image/', '', $imageInfo[0]);      
    $image = str_replace(' ', '+', $imageInfo[1]);
    $imageName = "post-".time().".".$imgExt;
    Storage::disk('public_feeds')->put($imageName, base64_decode($image));

You can create 'public_feeds' in laravel's filesystem.php-

   'public_feeds' => [
        'driver' => 'local',
        'root'   => public_path() . '/uploads/feeds',
    ],
2
if($_SERVER['REQUEST_METHOD']=='POST'){
$image_no="5";//or Anything You Need
$image = $_POST['image'];
$path = "uploads/".$image_no.".png";

$status = file_put_contents($path,base64_decode($image));
if($status){
 echo "Successfully Uploaded";
}else{
 echo "Upload failed";
}
}
Julfikar
  • 1,100
  • 1
  • 14
  • 30
  • 5
    Welcome to Stack Overflow! Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation [would greatly improve](//meta.stackexchange.com/q/114762) its long-term value by showing *why* this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Toby Speight Oct 12 '17 at 13:13
  • This is not an answer or even relevant to the original question. – Austin Brunkhorst Jan 13 '18 at 20:08
0

This code worked for me.

<?php
$decoded = base64_decode($base64);
$file = 'invoice.pdf';
file_put_contents($file, $decoded);

if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'.basename($file).'"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    readfile($file);
    exit;
}
?>
Fazil Raza
  • 105
  • 2
  • 5
-4
$datetime = date("Y-m-d h:i:s");
$timestamp = strtotime($datetime);
$image = $_POST['image'];
$imgdata = base64_decode($image);
$f = finfo_open();
$mime_type = finfo_buffer($f, $imgdata, FILEINFO_MIME_TYPE);
$temp=explode('/',$mime_type);
$path = "uploads/$timestamp.$temp[1]";
file_put_contents($path,base64_decode($image));
echo "Successfully Uploaded->>> $timestamp.$temp[1]";

This will be enough for image processing. Special thanks to Mr. Dev Karan Sharma

Manish
  • 31
  • 3