Come salvare una tela HTML5 come immagine su un server?


261

Sto lavorando a un progetto di arte generativa in cui vorrei consentire agli utenti di salvare le immagini risultanti da un algoritmo. L'idea generale è:

  • Crea un'immagine su una tela HTML5 usando un algoritmo generativo
  • Al termine dell'immagine, consentire agli utenti di salvare l'area di disegno come file di immagine sul server
  • Consentire all'utente di scaricare l'immagine o aggiungerla a una galleria di pezzi di prodotto utilizzando l'algoritmo.

Tuttavia, sono bloccato sul secondo passo. Dopo un po 'di aiuto da parte di Google, ho trovato questo post sul blog , che sembrava essere esattamente quello che volevo:

Che ha portato al codice 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);
}

e PHP corrispondente (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);
}
?>

Ma questo non sembra fare nulla.

Altro Google cerca questo post sul blog che si basa sul tutorial precedente. Non molto diverso, ma forse vale la pena provare:

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

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

Questo crea un file (yay) ma è danneggiato e non sembra contenere nulla. Sembra inoltre essere vuoto (dimensione del file pari a 0).

C'è qualcosa di veramente ovvio che sto sbagliando? Il percorso in cui sto memorizzando il mio file è scrivibile, quindi non è un problema, ma sembra che non stia succedendo nulla e non sono davvero sicuro di come eseguire il debug di questo.

modificare

Seguendo il collegamento di Salvidor Dali ho modificato la richiesta AJAX in:

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

E ora il file immagine è stato creato e non è vuoto! Sembra che il tipo di contenuto sia importante e che modificarlo per x-www-form-urlencodedconsentire l'invio dei dati dell'immagine.

La console restituisce la stringa (piuttosto grande) di codice base64 e il file di dati è ~ 140 kB. Tuttavia, non riesco ancora ad aprirlo e sembra non essere formattato come immagine.


Il primo post sul blog fornito viene utilizzato ajax.send(canvasData );mentre lo usi come ajax.send("imgData="+canvasData);. Pertanto $GLOBALS["HTTP_RAW_POST_DATA"]non sarà quello che ti aspetti, probabilmente dovresti usare $_POST['imgData'].
Matteus Hemström,


Diodeus: sto già usando la tecnica suggerita in quel thread; tuttavia, non sono riusciti a fornire ulteriori dettagli sull'implementazione ed è qui che mi sto bloccando.
Nathan Lachenmyer,

Quando faccio eco alle informazioni sul file ( $datanel secondo codice php) tutto ciò che mi viene restituito è una riga vuota. Perché dovrebbe essere? Sembra che forse i dati inviati non siano corretti, ma sembra che li sto inviando proprio come mostrano gli esempi ...
Nathan Lachenmyer,

Il codice PHP può essere semplificato e reso più robusto utilizzando invece PHP-FileUpload con il suo DataUriUploadcomponente. È documentato qui e viene fornito con diversi mezzi aggiuntivi di convalida e applicazione della sicurezza.
Caw

Risposte:


242

Ecco un esempio di come ottenere ciò di cui hai bisogno:

1) Disegna qualcosa (tratto dal tutorial della tela )

<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) Converti l'immagine su tela nel formato URL (base64)

var dataURL = canvas.toDataURL();

3) Invialo al tuo server tramite 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) Salva base64 sul tuo server come immagine (ecco come farlo in PHP , le stesse idee sono in ogni lingua. Il lato server in PHP può essere trovato qui ):


1
Voglio dire che il file è lì ma non si apre nel mio browser o in qualsiasi visualizzatore di immagini se lo scarico.
Nathan Lachenmyer,

You send it to your server as an ajax requestquesto metodo richiede la conferma dell'utente? Oppure posso inviare silenziosamente l'immagine dalla tela al server?
MyTitle,

Viene visualizzato un errore sulla console Web. [16: 53: 43.227] SecurityError: l'operazione non è sicura. @ sharevi.com/staging/canvas.html:43 the Questa connessione non è sicura . C'è qualcosa che deve essere fatto? /// AGGIORNAMENTO Penso di sapere perché, stavo usando immagini tra domini
Nikolaos Vassos

1
ma alpha è 0, il che lo rende non riempito, che è completamente trasparente. non importa quale sia il primo valore di tre.
Abel D,

68

Ci ho giocato due settimane fa, è molto semplice. L'unico problema è che tutti i tutorial parlano solo di salvare l'immagine localmente. Ecco come l'ho fatto:

1) Ho impostato un modulo in modo da poter utilizzare un metodo POST.

2) Quando l'utente ha finito di disegnare, può fare clic sul pulsante "Salva".

3) Quando si fa clic sul pulsante, prendo i dati dell'immagine e li inserisco in un campo nascosto. Successivamente invio il modulo.

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

4) Quando viene inviato il modulo ho questo piccolo 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']);
?>

1
va bene se uso canvas.toDataURL ()? Ho un'estensione di file dinamica, come jpeg, png, gif. Ho provato canvas.toDataURL ('image / jpg') ma non funziona. Cosa c'è che non va?
Ivo San,

Stavo ottenendo un errore 413 (richiesta troppo grande) nel tentativo di inviare l'immagine dalla tela di AJAX. Quindi questo approccio mi ha aiutato a ottenere le immagini.
Raiyan,

1
Qualche motivo per cui questo mi sta dando un file png 0kb?
prismspecs,

3
Per jpg dovresti fare canvas.toDataURL ('image / jpeg') - almeno questo è quello che ha fatto per me su Chrome.
Chris,

Grazie, è quello di cui avevo bisogno: D
FosAvance

21

Penso che dovresti trasferire l'immagine in base64 all'immagine con BLOB, perché quando usi l'immagine in base64, ci vogliono molte linee di log o molte linee invieranno al server. Con BLOB, è solo il file. Puoi usare questo codice qui sotto:

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

E il codice tela qui:

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

Dopodiché puoi usare ajax con 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

Questo codice utilizza la sintassi di CoffeeScript.

se vuoi usare javascript, per favore incolla il codice su http://js2.coffee


12

Invia immagine tela a PHP:

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

Ecco lo 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;
?>

9

Se si desidera salvare i dati derivati ​​da una canvas.toDataURL()funzione Javascript , è necessario convertire gli spazi vuoti in caratteri. In caso contrario, i dati decodificati sono danneggiati:

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

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


7

Ho lavorato su qualcosa di simile. Ho dovuto convertire l'immagine con tela codificata Base64 in 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

Oltre alla risposta di Salvador Dalì:

sul lato server non dimenticare che i dati arrivano in formato stringa base64 . È importante perché in alcuni linguaggi di programmazione devi dire esplicitamente che questa stringa deve essere considerata come byte non come semplice stringa Unicode.

Altrimenti la decodifica non funzionerà: l'immagine verrà salvata ma sarà un file illeggibile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.