web-dev-qa-db-fra.com

Comment enregistrer un canevas HTML5 en tant qu'image sur un serveur?

Je travaille sur un projet d'art génératif dans lequel je voudrais permettre aux utilisateurs de sauvegarder les images résultantes d'un algorithme. L'idée générale est:

  • Créer une image sur un canevas HTML5 à l'aide d'un algorithme génératif
  • Une fois l'image terminée, autorisez les utilisateurs à enregistrer la toile en tant que fichier image sur le serveur.
  • Autoriser l'utilisateur à télécharger l'image ou à l'ajouter à une galerie de pièces produites à l'aide de l'algorithme.

Cependant, je suis bloqué sur la deuxième marche. Après une aide de Google, j'ai trouvé ceci article de blog , qui semblait être exactement ce que je voulais:

Ce qui a conduit au code JavaScript:

function saveImage() {
  var canvasData = canvas.toDataURL("image/png");
  var ajax = new XMLHttpRequest();

  ajax.open("POST", "testSave.php", false);
  ajax.onreadystatechange = function() {
    console.log(ajax.responseText);
  }
  ajax.setRequestHeader("Content-Type", "application/upload");
  ajax.send("imgData=" + canvasData);
}

et PHP correspondant (testSave.php):

<?php
if (isset($GLOBALS["HTTP_RAW_POST_DATA"])) {
  $imageData = $GLOBALS['HTTP_RAW_POST_DATA'];
  $filteredData = substr($imageData, strpos($imageData, ",") + 1);
  $unencodedData = base64_decode($filteredData);
  $fp = fopen('/path/to/file.png', 'wb');

  fwrite($fp, $unencodedData);
  fclose($fp);
}
?>

Mais cela ne semble rien faire du tout.

Plus de recherches sur Google révèle ceci article de blog qui est basé sur le précédent tutoriel. Pas très différent, mais peut-être la peine d'essayer:

$data = $_POST['imgData'];
$file = "/path/to/file.png";
$uri = substr($data,strpos($data, ",") + 1);

file_put_contents($file, base64_decode($uri));
echo $file;

Celui-ci crée un fichier (yay) mais il est corrompu et ne semble contenir aucun élément. Il semble également être vide (taille de fichier de 0).

Y at-il quelque chose de vraiment évident que je me trompe? Le chemin dans lequel je stocke mon fichier est accessible en écriture. Ce n’est donc pas un problème, mais rien ne semble se passer et je ne sais pas trop comment le déboguer.

Modifier

Après le lien de Salvidor Dali, j’ai modifié la demande AJAX:

function saveImage() {
  var canvasData = canvas.toDataURL("image/png");
  var xmlHttpReq = false;

  if (window.XMLHttpRequest) {
    ajax = new XMLHttpRequest();
  }
  else if (window.ActiveXObject) {
    ajax = new ActiveXObject("Microsoft.XMLHTTP");
  }

  ajax.open("POST", "testSave.php", false);
  ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  ajax.onreadystatechange = function() {
    console.log(ajax.responseText);
  }
  ajax.send("imgData=" + canvasData);
}

Et maintenant, le fichier image est créé et n’est pas vide! Il semble que le type de contenu compte et que le changer en x-www-form-urlencoded permette l'envoi des données d'image.

La console renvoie la chaîne (plutôt longue) de code base64 et le fichier de données mesure environ 140 ko. Cependant, je ne peux toujours pas l’ouvrir et il ne semble pas être formaté en tant qu’image.

232

Voici un exemple pour obtenir ce dont vous avez besoin:

1) Dessine quelque chose (tiré de tutoriel sur la toile )

<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');

    // begin custom shape
    context.beginPath();
    context.moveTo(170, 80);
    context.bezierCurveTo(130, 100, 130, 150, 230, 150);
    context.bezierCurveTo(250, 180, 320, 180, 340, 150);
    context.bezierCurveTo(420, 150, 420, 120, 390, 100);
    context.bezierCurveTo(430, 40, 370, 30, 340, 50);
    context.bezierCurveTo(320, 5, 250, 20, 250, 50);
    context.bezierCurveTo(200, 5, 150, 20, 170, 80);

    // complete custom shape
    context.closePath();
    context.lineWidth = 5;
    context.fillStyle = '#8ED6FF';
    context.fill();
    context.strokeStyle = 'blue';
    context.stroke();
</script>

2) Convertit l'image de la toile au format URL (base64)

var dataURL = canvas.toDataURL();

3) Envoyez-le à votre serveur via Ajax

$.ajax({
  type: "POST",
  url: "script.php",
  data: { 
     imgBase64: dataURL
  }
}).done(function(o) {
  console.log('saved'); 
  // If you want the file to be visible in the browser 
  // - please modify the callback in javascript. All you
  // need is to return the url to the file, you just saved 
  // and than put the image in your browser.
});

3) Enregistrez la base64 sur votre serveur en tant qu’image (voici comment procéder en PHP , les mêmes idées sont en toutes les langues. Le serveur dans PHP peut être trouvé ici ):

224
Salvador Dali

J'ai joué avec ça il y a deux semaines, c'est très simple. Le seul problème est que tous les tutoriels ne parlent que de la sauvegarde locale de l'image. Voici comment je l'ai fait:

1) J'ai créé un formulaire pour pouvoir utiliser une méthode POST.

2) Lorsque l'utilisateur a terminé de dessiner, il peut cliquer sur le bouton "Enregistrer".

3) Lorsque le bouton est cliqué, je prends les données d'image et les place dans un champ caché. Après cela, je soumets le formulaire.

document.getElementById('my_hidden').value = canvas.toDataURL('image/png');
document.forms["form1"].submit();

4) Lorsque le formulaire est soumis, j'ai ce petit script php:

<?php 
$upload_dir = somehow_get_upload_dir();  //implement this function yourself
$img = $_POST['my_hidden'];
$img = str_replace('data:image/png;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$data = base64_decode($img);
$file = $upload_dir."image_name.png";
$success = file_put_contents($file, $data);
header('Location: '.$_POST['return_url']);
?>
61
user568021

Je pense que vous devriez transférer l'image en base64 en image avec blob, car lorsque vous utilisez une image en base64, il faut beaucoup de lignes de journal ou beaucoup de lignes seront envoyées au serveur. Avec blob, c'est seulement le fichier. Vous pouvez utiliser ce code ci-dessous:

dataURLtoBlob = (dataURL) ->
  # Decode the dataURL
  binary = atob(dataURL.split(',')[1])
  # Create 8-bit unsigned array
  array = []
  i = 0
  while i < binary.length
    array.Push binary.charCodeAt(i)
    i++
  # Return our Blob object
  new Blob([ new Uint8Array(array) ], type: 'image/png')

Et le code de la toile ici:

canvas = document.getElementById('canvas')
file = dataURLtoBlob(canvas.toDataURL())

Après cela, vous pouvez utiliser ajax avec Form:

  fd = new FormData
  # Append our Canvas image file to the form data
  fd.append 'image', file
  $.ajax
    type: 'POST'
    url: '/url-to-save'
    data: fd
    processData: false
    contentType: false

Ce code utilise la syntaxe CoffeeScript.

si vous voulez utiliser javascript, veuillez coller le code dans http://js2.coffee

13
ThienSuBS

Envoyer l'image de la toile à PHP:

var photo = canvas.toDataURL('image/jpeg');                
$.ajax({
  method: 'POST',
  url: 'photo_upload.php',
  data: {
    photo: photo
  }
});

Voici le script PHP:
photo_upload.php

<?php

    $data = $_POST['photo'];
    list($type, $data) = explode(';', $data);
    list(, $data)      = explode(',', $data);
    $data = base64_decode($data);

    mkdir($_SERVER['DOCUMENT_ROOT'] . "/photos");

    file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/photos/".time().'.png', $data);
    die;
?>
10
vikram jeet singh

Si vous souhaitez enregistrer des données dérivées d'une fonction Javascript canvas.toDataURL(), vous devez convertir les blancs en caractères plus. Si vous ne le faites pas, les données décodées sont corrompues:

<?php
  $encodedData = str_replace(' ','+',$encodedData);
  $decocedData = base64_decode($encodedData);
?>

http://php.net/manual/ro/function.base64-decode.php

7
MaloMax

En plus de la réponse de Salvador Dali:

côté serveur, n'oubliez pas que les données sont au format base64 chaîne. C'est important parce que dans certains langages de programmation, vous devez explicitement dire que cette chaîne doit être considérée comme octets pas une simple chaîne Unicode.

Sinon, le décodage ne fonctionnera pas: l'image sera sauvegardée mais ce sera un fichier illisible.

4
Ardine

J'ai travaillé sur quelque chose de similaire. Devait convertir le canevas codé en Base64 image en Uint8Array Blob.

function b64ToUint8Array(b64Image) {
   var img = atob(b64Image.split(',')[1]);
   var img_buffer = [];
   var i = 0;
   while (i < img.length) {
      img_buffer.Push(img.charCodeAt(i));
      i++;
   }
   return new Uint8Array(img_buffer);
}

var b64Image = canvas.toDataURL('image/jpeg');
var u8Image  = b64ToUint8Array(b64Image);

var formData = new FormData();
formData.append("image", new Blob([ u8Image ], {type: "image/jpg"}));

var xhr = new XMLHttpRequest();
xhr.open("POST", "/api/upload", true);
xhr.send(formData);
4
Joseph D.