Wie speichere ich einen HTML5-Canvas als Bild auf einem Server?

261

Ich arbeite an einem generativen Kunstprojekt, bei dem ich Benutzern ermöglichen möchte, die resultierenden Bilder von einem Algorithmus zu speichern. Die allgemeine Idee ist:

  • Erstellen Sie ein Bild auf einem HTML5-Canvas mithilfe eines generativen Algorithmus
  • Wenn das Bild fertig ist, können Benutzer die Zeichenfläche als Bilddatei auf dem Server speichern
  • Ermöglichen Sie dem Benutzer, das Bild entweder herunterzuladen oder einer Galerie von Stücken hinzuzufügen, die mit dem Algorithmus erstellt wurden.

Ich stecke jedoch beim zweiten Schritt fest. Nach etwas Hilfe von Google fand ich diesen Blog-Beitrag , der genau das zu sein schien, was ich wollte:

Was zum JavaScript-Code führte:

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

und entsprechendes PHP (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);
}
?>

Aber das scheint überhaupt nichts zu bewirken.

Mehr Googeln zeigt diesen Blog-Beitrag an, der auf dem vorherigen Tutorial basiert. Nicht sehr unterschiedlich, aber vielleicht einen Versuch wert:

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

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

Dieser erstellt eine Datei (yay), aber sie ist beschädigt und scheint nichts zu enthalten. Es scheint auch leer zu sein (Dateigröße 0).

Gibt es etwas wirklich Offensichtliches, das ich falsch mache? Der Pfad, in dem ich meine Datei speichere, ist beschreibbar, das ist also kein Problem, aber es scheint nichts zu passieren, und ich bin mir nicht sicher, wie ich das debuggen soll.

Bearbeiten

Nach Salvidor Dalis Link habe ich die AJAX-Anfrage wie folgt geändert:

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

Und jetzt wird die Bilddatei erstellt und ist nicht leer! Es scheint, als ob der Inhaltstyp wichtig ist und dass das Ändern des Inhaltstyps x-www-form-urlencodeddas Senden der Bilddaten ermöglicht.

Die Konsole gibt die (ziemlich große) Zeichenfolge von base64-Code zurück und die Datendatei ist ~ 140 kB groß. Ich kann es jedoch immer noch nicht öffnen und es scheint nicht als Bild formatiert zu sein.

nathan lachenmyer
quelle
Der erste Blog-Beitrag, den Sie bereitgestellt haben, wird verwendet, ajax.send(canvasData );während Sie ihn verwenden ajax.send("imgData="+canvasData);. Daher $GLOBALS["HTTP_RAW_POST_DATA"]wird nicht das sein, was Sie erwarten, sollten Sie wahrscheinlich verwenden $_POST['imgData'].
Matteus Hemström
stackoverflow.com/questions/3592164/…
Diodeus - James MacFarlane
Diodeus: Ich verwende bereits die Technik, die in diesem Thread vorgeschlagen wurde. Sie haben jedoch keine weiteren Details zur Implementierung angegeben, und hier stecke ich fest.
Nathan Lachenmyer
Wenn ich die Dateiinformationen ( $dataim zweiten PHP-Code) wiedergebe, erhalte ich nur eine leere Zeile zurück. Warum sollte das so sein? Es scheint, als ob die gesendeten Daten vielleicht nicht korrekt sind, aber es scheint, dass ich sie sende, genau wie die Beispiele zeigen ...
Nathan Lachenmyer
Der PHP-Code kann vereinfacht und robuster gemacht werden, indem stattdessen PHP-FileUpload mit seiner DataUriUploadKomponente verwendet wird. Es ist hier dokumentiert und verfügt über mehrere zusätzliche Methoden zur Validierung und Durchsetzung der Sicherheit.
Caw

Antworten:

242

Hier ist ein Beispiel, wie Sie das erreichen, was Sie brauchen:

1) Zeichne etwas (aus dem Canvas-Tutorial )

<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) Konvertieren Sie das Canvas-Bild in das URL-Format (base64).

var dataURL = canvas.toDataURL();

3) Senden Sie es über Ajax an Ihren Server

$.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) Speichern Sie base64 auf Ihrem Server als Image (so geht 's in PHP , die gleichen Ideen gibt es in jeder Sprache. Die Serverseite in PHP finden Sie hier ):

Salvador Dali
quelle
1
Ich meine, dass die Datei vorhanden ist, aber nicht in meinem Browser oder einem Bildbetrachter geöffnet wird, wenn ich sie herunterlade.
Nathan Lachenmyer
You send it to your server as an ajax requestErfordert diese Methode eine Bestätigung des Benutzers? Oder kann ich stillschweigend Bilder von der Leinwand an den Server senden?
MyTitle
Ich erhalte eine Fehlermeldung in der Webkonsole. [16: 53: 43.227] SecurityError: Der Vorgang ist unsicher. @ sharevi.com/staging/canvas.html:43 the Diese Verbindung ist unsicher. Gibt es etwas, das getan werden muss? /// UPDATE Ich glaube ich weiß warum, ich habe domänenübergreifende Bilder verwendet
Nikolaos Vassos
1
aber alpha ist 0, was es nicht füllend macht, das ist völlig transparent. egal was die ersten drei Werte sind.
Abel D
68

Ich habe vor zwei Wochen damit gespielt, es ist sehr einfach. Das einzige Problem ist, dass in allen Tutorials nur über das lokale Speichern des Bildes gesprochen wird. So habe ich es gemacht:

1) Ich habe ein Formular eingerichtet, damit ich eine POST-Methode verwenden kann.

2) Wenn der Benutzer mit dem Zeichnen fertig ist, kann er auf die Schaltfläche "Speichern" klicken.

3) Wenn auf die Schaltfläche geklickt wird, nehme ich die Bilddaten und lege sie in ein verstecktes Feld. Danach sende ich das Formular.

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

4) Wenn das Formular gesendet wird, habe ich dieses kleine PHP-Skript:

<?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']);
?>
user568021
quelle
1
Ist es in Ordnung, wenn ich canvas.toDataURL () verwende? Ich habe eine dynamische Dateierweiterung wie JPEG, PNG, GIF. Ich habe canvas.toDataURL ('image / jpg') ausprobiert, aber es funktioniert nicht. Was ist los mit dir?
Ivo San
Beim Versuch, ein Bild von AJAX von der Leinwand zu senden, wurde der Fehler 413 (Anfrage zu groß) angezeigt. Dann half mir dieser Ansatz, die Bilder durchzubringen.
Raiyan
1
Gibt es einen Grund, warum dieser mir eine 0kb PNG-Datei gibt?
Prismspecs
3
Für jpg müssten Sie canvas.toDataURL ('image / jpeg') ausführen - zumindest hat es das für mich in Chrome getan.
Chris
Danke, das habe ich gebraucht: D
FosAvance
21

Ich denke, Sie sollten das Image in Base64 auf das Image mit Blob übertragen, da bei Verwendung des Base64-Images viele Protokollzeilen erforderlich sind oder viele Zeilen an den Server gesendet werden. Mit Blob ist es nur die Datei. Sie können diesen Code unten verwenden:

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

Und Canvas-Code hier:

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

Danach können Sie Ajax mit Form verwenden:

  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

Dieser Code verwendet die CoffeeScript-Syntax.

Wenn Sie Javascript verwenden möchten, fügen Sie den Code bitte in http://js2.coffee ein

ThienSuBS
quelle
12

Leinwandbild an PHP senden:

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

Hier ist das PHP-Skript:
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;
?>
vikram jeet singh
quelle
9

Wenn Sie Daten speichern möchten, die von einer Javascript- canvas.toDataURL()Funktion abgeleitet sind, müssen Sie Leerzeichen in Plusses konvertieren. Wenn Sie dies nicht tun, sind die dekodierten Daten beschädigt:

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

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

MaloMax
quelle
7

Ich habe an etwas ähnlichem gearbeitet. Musste Canvas Base64-codiertes Bild in konvertieren 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);
Joseph D.
quelle
4

Zusätzlich zu Salvador Dalis Antwort:

Vergessen Sie auf der Serverseite nicht, dass die Daten im Base64- Zeichenfolgenformat vorliegen. Dies ist wichtig, da Sie in einigen Programmiersprachen explizit angeben müssen, dass diese Zeichenfolge als Byte und nicht als einfache Unicode-Zeichenfolge betrachtet werden soll.

Andernfalls funktioniert die Dekodierung nicht: Das Bild wird gespeichert, es handelt sich jedoch um eine nicht lesbare Datei.

Ardine
quelle