Cómo guardar una imagen PNG del lado del servidor, desde una cadena de datos base64

212

Estoy usando la herramienta JavaScript "Canvas2Image" de Nihilogic para convertir dibujos de lienzo en imágenes PNG. Lo que necesito ahora es convertir esas cadenas base64 que genera esta herramienta, en archivos PNG reales en el servidor, usando PHP.

En resumen, lo que estoy haciendo actualmente es generar un archivo en el lado del cliente usando Canvas2Image, luego recuperar los datos codificados en base64 y enviarlos al servidor usando 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
    }
});

En este punto, "hidden.php" recibe un bloque de datos que parece datos: image / png; base64, iVBORw0KGgoAAAANSUhEUgAABE ...

A partir de este momento, estoy bastante perplejo. Por lo que he leído, creo que se supone que debo usar la función imagecreatefromstring de PHP , pero no estoy seguro de cómo crear una imagen PNG real a partir de la cadena codificada en base64 y almacenarla en mi servidor. Por favor ayuda!

Andrei Oniga
fuente
necesitas analizarlo. puede extraer el tipo de imagen desde allí y luego usar base64_decode y guardar esa cadena en un archivo por su tipo de imagen
Constantin
@Constantine ¿Puedes ser más específico, por favor?
Andrei Oniga
77
$ datos = $ _REQUEST ['base64data']; $ imagen = explotar ('base64,', $ datos); file_put_contents ('img.png', base64_decode ($ imagen [1]));
Constantin
puede publicar el código completo, desde la instantánea y hasta que envíe los datos, no funciona para mí.
Ofir Attia

Respuestas:

437

Necesita extraer los datos de la imagen base64 de esa cadena, decodificarla y luego puede guardarla en el disco, no necesita GD ya que ya es un 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);

Y como una frase:

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

Un método eficiente para extraer, decodificar y verificar errores es:

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 = 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);
drew010
fuente
Tienes $ type en tu ejemplo. ¿Cuál debería ser ese valor?
Jon
1
@Jon $typerecibe un valor de la explosión, dependiendo de si se trata de datos: image / jpg o data: image / png etc.
drew010
recibí este error: los caracteres utf-8 mal formados posiblemente están codificados incorrectamente ¿qué debo hacer?
aldo
@aldo Probablemente publique una nueva pregunta con código y detalles. Bastante imposible de decir basado en ese mensaje y sin ver su código o saber cómo se codifica la imagen y cómo se envía al script.
drew010
120

Prueba esto:

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

file_put_contents docs

Algún chico
fuente
44
Intenté esto pero incluye el data:image/png;base64,que rompe el archivo.
Michael J. Calkins
2
@MichaelCalkins me lo rompe también. La respuesta de drew010 parece ser la única solución que funciona de manera consistente.
David John Welsh el
24
Si incluye el, data:image/png;base64,entonces la cadena a la que está pasando base64_decodeno es válida base64. Solo necesita enviar los datos, que es lo que ilustra la respuesta de drew010. No hay nada roto con esta respuesta. No puede copiar y pegar sin comprender y esperar que "simplemente funcione".
Cypher
44
No funciona para el ejemplo proporcionado en la pregunta, que no es un contenido base64, y primero debe dividirse en partes (consulte la respuesta aceptada).
Benjamin Piette
Mi imagen ha sido guardada con éxito. pero cuando escribo url de la imagen veo una imagen en blanco. Estoy seguro de que mi dataURL es correcto, porque lo probé usando window.open (dataURL). ¿Por qué una imagen en blanco?
partho
45

Tuve que reemplazar espacios con símbolos más str_replace(' ', '+', $img);para que esto funcionara.

Aquí está el código completo

$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);

Espero que ayude.

Ben
fuente
1
Tu línea $img = str_replace(' ', '+', $img);me ayudó mucho, gracias! ¿Por qué tengo que convertir el espacio a "+" antes?
Sven
19

Vale la pena decir que el tema discutido está documentado en RFC 2397 - El esquema de URL de "datos" ( https://tools.ietf.org/html/rfc2397 )

Debido a esto, PHP tiene una forma nativa de manejar dichos datos: "data: stream wrapper" ( http://php.net/manual/en/wrappers.data.php )

Para que pueda manipular fácilmente sus datos con secuencias PHP:

$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);
Vladimir Posvistelik
fuente
2
Me gusta esta respuesta, debería tener más votos, está usando funciones nativas, ¡no hay manipulación sucia de datos caseros! Aunque, ¿alguna idea sobre actuaciones? ¿Es, como cabría esperar, más rápido que preg_replace+ file_put_contents?
Bonswouar
1
@Bonswouar Gracias. Con respecto a su pregunta: en mi opinión, la mejor manera de garantizar que su aplicación no tenga problemas de rendimiento sería medir su rendimiento en su caso particular (según el entorno, el tamaño de archivo esperado y permitido, etc.). Podríamos intentar jugar con el código aquí y mostrar números en la respuesta, pero la verdad es que para su caso exacto podría traer más daño que un resultado positivo. Siempre es mejor asegurarse por su cuenta (especialmente si estamos hablando de partes críticas de rendimiento de la aplicación).
Vladimir Posvistelik
11

Bueno, su solución anterior depende de que la imagen sea un archivo jpeg. Para una solución general usé

$img = $_POST['image'];
$img = substr(explode(";",$img)[1], 7);
file_put_contents('img.png', base64_decode($img));
diablo_io
fuente
11

Tomada la idea @ dre010, la he extendido a otra función que funciona con cualquier tipo de imagen: PNG, JPG, JPEG o GIF y le da un nombre único al nombre de archivo

La función separa los datos de imagen y el tipo de imagen

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
fuente
5

Solución unidireccional.

$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
fuente
5

basado en el ejemplo drew010 hice un ejemplo de trabajo para facilitar la comprensión.

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
fuente
4

prueba esto...

$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
fuente
4

Este código funciona para mí, verifique el siguiente código:

<?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
fuente
0

Esta función debería funcionar. esto tiene el parámetro de foto que contiene la cadena base64 y también la ruta a un directorio de imagen existente si ya tiene una imagen existente que desea desvincular mientras guarda la nueva.

 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
        }
    }
}
Salvador Dela
fuente
0

Es sencillo :

Imaginemos que está intentando cargar un archivo dentro de js framework, solicitud ajax o aplicación móvil (lado del cliente)

  1. En primer lugar, envía un atributo de datos que contiene una cadena codificada en base64.
  2. En el lado del servidor, debe decodificarlo y guardarlo en una carpeta de proyecto local.

Aquí cómo hacerlo usando 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
fuente
0

Si desea cambiar el nombre de las imágenes al azar y almacenar la ruta de la imagen en la base de datos como blob y la imagen en sí en las carpetas, esta solución lo ayudará. Los usuarios de su sitio web pueden almacenar tantas imágenes como quieran, mientras que las imágenes se renombrarán aleatoriamente por razones de seguridad.

Código php

Genere varchars aleatorios para usar como nombre de imagen.

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).

Obtenga la extensión de datos de imagen y la parte base_64 (parte después de los datos: image / png; base64,) de la imagen.

$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. :)
Chikeluba Anusionwu
fuente
0

PHP ya tiene un tratamiento justo base64 -> transformación de archivo

Solía ​​hacerlo de esta manera:

$blob=$_POST['blob']; // base64 coming from an url, for example
$blob=file_get_contents($blob);
$fh=fopen("myfile.png",'w'); // be aware, it'll overwrite!
fwrite($fh,$blob);
fclose($fh);
echo '<img src=myfile.png>'; // just for the check
PYK
fuente