Comment sauvegarder une toile HTML5 comme une image sur un serveur?

je travaille sur un projet artistique génératif où je voudrais permettre aux utilisateurs de sauvegarder les images résultantes à partir d'un algorithme. L'idée générale est:

  • créer une image sur une toile HTML5 en utilisant un algorithme génératif
  • Lorsque l'image est terminée, permettent aux utilisateurs d'enregistrer la toile comme un fichier image sur le serveur
  • Permettent à l'utilisateur de télécharger l'image ou l'ajouter à une galerie de pièces de produits à l'aide de l'algorithme.

cependant, je suis coincé sur la deuxième étape. Après un peu d'aide de Google, j'ai trouvé ce post de blog , qui a semblé être exactement ce que je voulais:

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 googling turns en haut de ce post de blog qui est basé sur le tutoriel précédent. Pas très différent, mais peut-être à 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 pas contenir quoi que ce soit. Il semble aussi être vide (Taille du fichier de 0).

y a-t-il quelque chose de vraiment évident que je me trompe? Le chemin où je stocke mon fichier est accessible en écriture, donc ce n'est pas un problème, mais rien ne semble se passer et je ne suis pas vraiment sûr de savoir comment déboguer ce.

Modifier

suite au lien de Salvidor Dali j'ai changé la requête AJAX pour être:

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 fait de le changer en x-www-form-urlencoded a permis d'envoyer les données d'image.

la console renvoie la chaîne (plutôt grande) de code base64 et le fichier de données est ~140 kB. Cependant, je ne peux toujours pas l'ouvrir et il ne semble pas être formaté comme une image.

206
demandé sur Xufox 2012-11-02 19:18:46

7 réponses

voici un exemple pour réaliser ce dont vous avez besoin:

1) dessiner quelque chose (tiré de tutoriel sur 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) Convertir en toile d'image de format d'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) enregistrer base64 sur votre serveur comme un image (voici comment faire en PHP , les mêmes idées sont dans toutes les langues. Côté serveur en PHP peut être trouvé ici ):

202
répondu Salvador Dali 2017-05-23 12:18:22

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 sauvegarder l'image localement. C'est comme ça que je l'ai fait:

1) j'ai mis en place un formulaire pour pouvoir utiliser une méthode de POST.

2) Lorsque l'Utilisateur a terminé le dessin, il peut cliquer sur le bouton "Enregistrer".

3) lorsque le bouton est cliqué je prends les données de l'image et les mets dans un champ caché. Après cela, j'ai envoyer 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']);
?>
56
répondu user568021 2015-07-13 18:24:00

je pense que vous devriez transférer l'image dans base64 à l'image avec blob, parce que lorsque vous utilisez base64 image, il faut beaucoup de lignes de log ou beaucoup de ligne va envoyer 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 Code canvas ici:

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

après cela, vous pouvez utiliser ajax avec la forme:

  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.

10
répondu ThienSuBS 2016-12-26 15:05:40

si vous voulez enregistrer des données qui sont dérivées de la fonction Javascript canvas.toDataURL() , vous devez convertir des blancs en 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
répondu MaloMax 2014-05-16 22:03:09

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;
?>
5
répondu vikram jeet singh 2017-12-17 22:45:08

en plus de la réponse de Salvador Dali:

côté serveur n'oubliez pas que les données sont au format base64 string . C'est important parce que dans certains langages de programmation vous devez expliquer que cette chaîne doit être considérée comme bytes 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
répondu Ardine 2014-10-24 15:27:20

j'ai travaillé sur quelque chose de similaire. Avait pour convertir toile Base64 image 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);
3
répondu codekaizer 2018-03-23 01:19:18