271 votes

Comment enregistrer une image PNG côté serveur, à partir d'un URI de données base64.

Je suis en train d'utiliser l'outil JavaScript "Canvas2Image" de Nihilogic pour convertir des dessins de canvas 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, c'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 :

// Générer le fichier image
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 à data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABE...

À partir de ce point, je suis un peu perdu. D'après ce que j'ai lu, je crois que je suis censé utiliser la fonction imagecreatefromstring de PHP, mais je ne suis pas sûr de comment créer une image PNG réelle à partir de la chaîne encodée en base64 et la stocker sur mon serveur. Aidez-moi, s'il vous plaît!

0 votes

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 selon votre type d'image

0 votes

@Constantine Pouvez-vous être plus précis, s'il vous plaît?

7 votes

$data = $_REQUEST['base64data']; $image = explode('base64,', $data); file_put_contents('img.png', base64_decode($image[1]));

539voto

drew010 Points 32825

Vous devez extraire les données d'image base64 de cette chaîne, les décoder puis 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 seule 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 :

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('type d\'image invalide');
    }
    $data = str_replace( ' ', '+', $data );
    $data = base64_decode($data);

    if ($data === false) {
        throw new \Exception('échec de base64_decode');
    }
} else {
    throw new \Exception('n\'a pas fait correspondance avec les données d\'image URI');
}

file_put_contents("img.{$type}", $data);

0 votes

Vous avez $type dans votre exemple. Quelle devrait être cette valeur?

1 votes

@Jon $type reçoit une valeur de l'explosion, selon qu'il s'agisse de data:image/jpg ou data:image/png, etc.

0 votes

J'ai eu cette erreur : caractères UTF-8 mal formés, peut-être incorrectement encodés, que dois-je faire?

143voto

Some Guy Points 8752

Essayer ceci:

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

Documentation de file_put_contents

8 votes

J'ai essayé cela mais cela inclut le data:image/png;base64, ce qui casse le fichier.

3 votes

@MichaelCalkins Me brise également, drew010 semble être la seule solution qui fonctionne de manière cohérente.

34 votes

Si vous incluez le data:image/png;base64,, alors la chaîne que vous passez à base64_decode n'est pas du base64 valide. Vous devez uniquement envoyer les données, ce que la réponse de drew010 illustre. Il n'y a rien de cassé avec cette réponse. Vous ne pouvez pas copier-coller sans comprendre et vous attendre à ce que cela "fonctionne simplement".

55voto

Ben Points 324

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

Voici le code complet

$img = $_POST['img']; // Vos données '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 vous aidera.

4 votes

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

1 votes

Est-il nécessaire de valider ces données d'une manière ou d'une autre ? comme vous le feriez normalement avec les données $_FILES ? et si oui, comment est-ce possible

23voto

v2p Points 2196

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

En raison de cela, PHP dispose d'une manière native de gérer de telles données - "enveloppeur de flux de 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);

3 votes

J'aime cette réponse, elle devrait avoir plus de votes, elle utilise des fonctions natives, pas de manipulation de données maison! Cependant, une idée sur les performances ? Est-ce, comme on pourrait s'y attendre, plus rapide que preg_replace + file_put_contents ?

2 votes

@Bonswouar Merci. En ce qui concerne votre question : à mon avis, la meilleure façon de garantir que votre application n'a pas de problèmes de performance serait de mesurer sa performance 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 des chiffres dans la réponse, mais la vérité est que pour votre cas précis, cela pourrait causer plus de tort que de bien. Il est toujours préférable de s'assurer par vous-même (surtout si nous parlons de parties critiques en termes de performance de l'application).

14voto

PolloZen Points 604

En reprenant l'idée de @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 fichier

La fonction sépare les données de l'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);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X