Jak zapisać płótno HTML5 Jako obraz na serwerze?

Pracuję nad projektem sztuki generatywnej, w którym chciałbym umożliwić użytkownikom zapisywanie wynikowych obrazów z algorytmu. Ogólna idea to:

  • tworzenie obrazu na płótnie HTML5 przy użyciu algorytmu generatywnego
  • gdy obraz jest ukończony, Pozwól użytkownikom zapisać płótno jako plik obrazu na serwerze
  • Pozwól użytkownikowi pobrać obraz lub dodać go do galerii kawałków wyprodukowanych za pomocą algorytmu.

Jednak utknąłem na drugi krok. Po Pomocy Google znalazłem ten wpis na blogu, który wydawał się być dokładnie tym, czego chciałem:

Co doprowadziło do kodu 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);
}

I odpowiadające im 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);
}
?>

Ale to wydaje się nic nie robić.

Więcej googlowania pojawia się w tym blogu, który jest oparty na poprzednim tutorialu. Nie bardzo różne, ale może warto spróbować:

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

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

Ten tworzy plik (yay), ale to uszkodzony i wydaje się, że nic nie zawiera. Również wydaje się być pusty (Rozmiar pliku 0).

Czy jest coś oczywistego, że robię źle? Ścieżka, na której przechowuję mój plik, jest zapisywalna, więc nie jest to problem, ale wydaje się, że nic się nie dzieje i nie jestem pewien, jak to debugować.

Edit

Po linku Salvidora Dali zmieniłem żądanie AJAX na:

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

I teraz plik obrazu jest tworzony i nie jest pusty! Wydaje się, że treść typ ma znaczenie, a zmiana go na x-www-form-urlencoded pozwoliła na wysłanie danych obrazu.

Konsola zwraca (dość duży) ciąg kodu base64, a plik danych wynosi ~140 kB. Jednak nadal nie mogę go otworzyć i wydaje się, że nie jest sformatowany jako obraz.

Author: Xufox, 2012-11-02

7 answers

Oto przykład, jak osiągnąć to, czego potrzebujesz:

1) narysuj coś (zaczerpnięte z 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) Konwertuj obraz płótna do formatu URL (base64)

var dataURL = canvas.toDataURL();

3) wyślij go na serwer przez 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) Zapisz base64 na serwerze jako obraz (Oto Jak to zrobić w PHP, te same pomysły są w każdym języku. Stronę serwera w PHP można znaleźć tutaj):

 202
Author: Salvador Dali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:22

Grałem tym dwa tygodnie temu, to bardzo proste. Jedynym problemem jest to, że wszystkie samouczki mówią tylko o zapisywaniu obrazu lokalnie. Tak to zrobiłem:

1) skonfigurowałem formularz, aby móc korzystać z metody POST.

2) po zakończeniu rysowania użytkownik może kliknąć przycisk" Zapisz".

3) Po kliknięciu przycisku biorę dane obrazu i umieszczam je w ukrytym polu. Następnie przesyłam formularz.

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

4) po przesłaniu formularza mam taki mały php skrypt:

<?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
Author: user568021,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-13 18:24:00

Myślę, że powinieneś przenieść obraz w base64 na obraz z blobem, ponieważ gdy używasz obrazu base64, zajmuje to dużo linii logów lub dużo linii wyśle na serwer. Z blob, to tylko plik. Możesz użyć tego kodu poniżej:

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

I Kod płótna tutaj:

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

Następnie możesz użyć ajax z formularzem:

  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

Ten kod używa składni CoffeeScript.

 10
Author: ThienSuBS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 15:05:40

Jeśli chcesz zapisać dane pochodzące z funkcji Javascript canvas.toDataURL(), musisz przekonwertować spacje na plusy. Jeśli tego nie zrobisz, zdekodowane dane zostaną uszkodzone:

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

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

 7
Author: MaloMax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-16 22:03:09

Wyślij obraz canvas do PHP:

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

Oto skrypt 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
Author: vikram jeet singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-17 22:45:08

Oprócz odpowiedzi Salvadora Dali:

Po stronie serwera nie zapomnij, że dane są w formacie base64 string . Jest to ważne, ponieważ w niektórych językach programowania należy wyraźnie powiedzieć, że łańcuch ten powinien być traktowany jako bajty , a nie prosty łańcuch Unicode.

W Przeciwnym Razie dekodowanie nie zadziała: obraz zostanie zapisany, ale będzie to plik nieczytelny.

 4
Author: Ardine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-24 15:27:20

Pracowałem nad czymś podobnym. Musiałem przekonwertować obraz canvas kodowany base64 na 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
Author: codekaizer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-23 01:19:18