Comment enregistrer une image PNG côté serveur, à partir d'une chaîne de données base64

212

J'utilise l'outil JavaScript "Canvas2Image" de Nihilogic pour convertir des dessins de toile en images PNG. Ce dont j'ai besoin maintenant, c'est de transformer ces chaînes base64 que cet outil génère, en fichiers PNG réels sur le serveur, en utilisant PHP.

En bref, ce que je fais actuellement est de générer un fichier côté client en utilisant Canvas2Image, puis de récupérer les données encodées en base64 et de les envoyer au serveur en utilisant 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
    }
});

À ce stade, "hidden.php" reçoit un bloc de données qui ressemble à des données: image / png; base64, iVBORw0KGgoAAAANSUhEUgAABE ...

À partir de ce moment, je suis à peu près perplexe. D'après ce que j'ai lu, je pense que je suis censé utiliser la fonction imagecreatefromstring de PHP , mais je ne sais pas comment créer réellement une image PNG réelle à partir de la chaîne encodée en base64 et la stocker sur mon serveur. Veuillez aider!

Andrei Oniga
la source
vous devez l'analyser. vous pouvez extraire le type d'image à partir de là, puis utiliser base64_decode et enregistrer cette chaîne dans un fichier par votre type d'image
Constantin
@Constantine Pouvez-vous être plus précis, s'il vous plaît?
Andrei Oniga
7
$ data = $ _REQUEST ['base64data']; $ image = exploser ('base64,', $ data); file_put_contents ('img.png', base64_decode ($ image [1]));
Constantin
vous pouvez poster le code complet, à partir de l'instantané et jusqu'à ce que vous envoyiez les données, cela ne fonctionne pas pour moi.
Ofir Attia

Réponses:

437

Vous devez extraire les données d'image base64 de cette chaîne, les décoder et ensuite vous pouvez les enregistrer sur le disque, vous n'avez pas besoin de GD car c'est déjà 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);

Et en une ligne:

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

Une méthode efficace pour extraire, décoder et vérifier les erreurs est la suivante:

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
la source
Vous avez $ type dans votre exemple. Quelle devrait être cette valeur?
Jon
1
@Jon $typeobtient une valeur de l'explosion, selon qu'il s'agit de données: image / jpg ou de données: image / png etc.
drew010
j'ai obtenu cette erreur: caractères utf-8 malformés éventuellement encodés incorrectement que dois-je faire?
aldo
@aldo Postez probablement une nouvelle question avec le code et les détails. Assez impossible à dire sur la base de ce message et sans voir votre code ou savoir comment l'image est codée et comment elle est envoyée au script.
drew010
120

Essaye ça:

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

documentation de file_put_contents

Un gars
la source
4
J'ai essayé mais cela inclut le data:image/png;base64,qui casse le fichier.
Michael J.Calkins
2
@MichaelCalkins le brise aussi pour moi. La réponse de drew010 semble la seule solution qui fonctionne de manière cohérente.
David John Welsh
24
Si vous incluez le, data:image/png;base64,la chaîne vers laquelle vous passez base64_decoden'est pas valide en base64. Vous n'avez qu'à envoyer les données, c'est ce qu'illustre la réponse de drew010. Il n'y a rien de cassé avec cette réponse. Vous ne pouvez pas copier et coller sans comprendre et vous attendre à ce qu'il "fonctionne simplement".
Cypher
4
Ne fonctionne pas pour l'exemple fourni dans la question, qui n'est pas un contenu base64, et doit d'abord être divisé en morceaux (voir la réponse acceptée).
Benjamin Piette
mon image a été enregistrée avec succès. mais quand j'écris l'url de l'image, je vois une image vierge. Je suis sûr que mon dataURL est correct, car j'ai testé cela en utilisant window.open (dataURL). Pourquoi une image vierge?
partho
45

J'ai dû remplacer les espaces par des symboles plus str_replace(' ', '+', $img);pour que cela fonctionne.

Voici le code complet

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

J'espère que cela pourra aider.

Ben
la source
1
Votre ligne $img = str_replace(' ', '+', $img);m'a beaucoup aidé, merci! Pourquoi dois-je convertir l'espace en "+" avant?
Sven
19

Il vaut la peine de dire que le sujet discuté est documenté dans la RFC 2397 - Le schéma d'URL "data" ( https://tools.ietf.org/html/rfc2397 )

Pour cette raison, PHP a une manière native de gérer ces données - "data: stream wrapper" ( http://php.net/manual/en/wrappers.data.php )

Vous pouvez donc facilement manipuler vos données avec les flux 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
la source
2
J'aime cette réponse, elle devrait avoir plus de votes, elle utilise des fonctions natives, pas de sale manipulation de données maison! Mais une idée des performances? Est-ce, comme on pouvait s'y attendre, plus rapide que preg_replace+ file_put_contents?
Bonswouar
1
@Bonswouar Merci. Concernant votre question: à mon avis, la meilleure façon de vous assurer que votre application n'a pas de problèmes de performances serait de mesurer ses performances dans votre cas particulier (en fonction de l'environnement, de la taille de fichier attendue et autorisée, etc.). Nous pourrions essayer de jouer avec le code ici et afficher les chiffres dans la réponse, mais la vérité est que pour votre cas exact, cela pourrait causer plus de tort que de résultat positif. Il est toujours préférable de s'assurer par vous-même (surtout si nous parlons de parties critiques de la performance de l'application).
Vladimir Posvistelik
11

Eh bien, votre solution ci-dessus dépend de l'image étant un fichier jpeg. Pour une solution générale, j'ai utilisé

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

Prenant l'idée @ dre010, je l'ai étendue à une autre fonction qui fonctionne avec n'importe quel type d'image: PNG, JPG, JPEG ou GIF et donne un nom unique au nom de fichier

La fonction sépare les données d'image et le type d'image

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
la source
5

Solution linéaire.

$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
la source
5

basé sur l'exemple de drew010 j'ai fait un exemple de travail pour une compréhension facile.

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
la source
4

essaye ça...

$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
la source
4

Ce code fonctionne pour moi, vérifiez le code ci-dessous:

<?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
la source
0

Cette fonction devrait fonctionner. cela a le paramètre photo qui contient la chaîne base64 et également le chemin vers un répertoire d'images existant si vous avez déjà une image existante que vous souhaitez dissocier pendant que vous enregistrez la nouvelle.

 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
        }
    }
}
Sauveur Dela
la source
0

C'est simple :

Imaginons que vous essayez de télécharger un fichier dans le cadre js, la demande ajax ou l'application mobile (côté client)

  1. Tout d'abord, vous envoyez un attribut de données qui contient une chaîne codée en base64.
  2. Côté serveur, vous devez le décoder et l'enregistrer dans un dossier de projet local.

Voici comment le faire en utilisant 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
la source
0

Si vous souhaitez renommer des images de manière aléatoire et stocker à la fois le chemin de l'image sur la base de données en tant qu'objet blob et l'image elle-même dans des dossiers, cette solution vous aidera. Les utilisateurs de votre site Web peuvent stocker autant d'images qu'ils le souhaitent, tandis que les images seront renommées de manière aléatoire pour des raisons de sécurité.

Code PHP

Générez des varchars aléatoires à utiliser comme nom d'image.

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

Obtenez l'extension des données d'image et la partie base_64 (partie après les données: image / png; base64,) de l'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. :)
Chikeluba Anusionwu
la source
0

PHP a déjà un traitement équitable base64 -> transformation de fichier

J'utilise pour le faire en utilisant cette façon:

$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
la source