javascript - mostrar - ¿Cómo guardar un lienzo HTML5 como una imagen en un servidor?
javascript mostrar imagen (7)
Estoy trabajando en un proyecto de arte generativo en el que me gustaría permitir que los usuarios guarden las imágenes resultantes de un algoritmo. La idea general es:
- Crea una imagen en un lienzo HTML5 utilizando un algoritmo generativo.
- Cuando se complete la imagen, permita a los usuarios guardar el lienzo como un archivo de imagen en el servidor
- Permita que el usuario descargue la imagen o la agregue a una galería de piezas producidas usando el algoritmo.
Sin embargo, estoy atascado en el segundo paso. Después de la ayuda de Google, encontré esta publicación de blog , que parecía ser exactamente lo que quería:
Lo que llevó al código 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);
}
y PHP correspondiente (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);
}
?>
Pero esto no parece hacer nada en absoluto.
Más Google busca esta publicación de blog que se basa en el tutorial anterior. No muy diferente, pero tal vez vale la pena intentarlo:
$data = $_POST[''imgData''];
$file = "/path/to/file.png";
$uri = substr($data,strpos($data, ",") + 1);
file_put_contents($file, base64_decode($uri));
echo $file;
Este crea un archivo (yay) pero está dañado y no parece contener nada. También parece estar vacío (tamaño de archivo de 0).
¿Hay algo realmente obvio que estoy haciendo mal? La ruta donde estoy almacenando mi archivo es de escritura, por lo que no es un problema, pero no parece que ocurra nada y no estoy realmente seguro de cómo depurar esto.
Editar
Siguiendo el enlace de Salvidor Dalí, cambié la solicitud de AJAX para ser:
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);
}
¡Y ahora el archivo de imagen está creado y no está vacío! Parece como si el tipo de contenido fuera importante y que el cambio a x-www-form-urlencoded
permitiera el x-www-form-urlencoded
los datos de la imagen.
La consola devuelve la cadena (bastante grande) de código base64 y el archivo de datos es ~ 140 kB. Sin embargo, todavía no puedo abrirlo y parece que no está formateado como una imagen.
Además de la respuesta de Salvador Dalí:
en el lado del servidor, no olvide que los datos vienen en formato de cadena base64. Es importante porque en algunos lenguajes de programación debe decir de manera explícita que esta cadena debe considerarse como bytes, no como una simple cadena Unicode.
De lo contrario, la decodificación no funcionará: la imagen se guardará pero será un archivo ilegible.
Aquí hay un ejemplo de cómo lograr lo que necesitas:
1) Dibuja algo (tomado de tutorial de lienzo )
<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 imagen de lienzo a formato URL (base64)
var dataURL = canvas.toDataURL();
3) Envíalo a tu servidor a través de 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) Guarde base64 en su servidor como una imagen (aquí es cómo hacer esto en PHP , las mismas ideas están en todos los idiomas. El lado del servidor en PHP se puede encontrar aquí ):
Creo que deberías transferir la imagen en base64 a la imagen con blob, porque cuando usas la imagen base64, tomas muchas líneas de registro o una gran cantidad de líneas se enviarán al servidor. Con blob, solo el archivo. Puedes usar este código abajo:
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'')
Y código de lienzo aquí:
canvas = document.getElementById(''canvas'')
file = dataURLtoBlob(canvas.toDataURL())
Después de eso puedes usar 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
Este código utiliza la sintaxis de CoffeeScript.
Enviar imagen de lienzo a PHP:
var photo = canvas.toDataURL(''image/jpeg'');
$.ajax({
method: ''POST'',
url: ''photo_upload.php'',
data: {
photo: photo
}
});
Aquí está el 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;
?>
He trabajado en algo similar. Tuvo que convertir la imagen codificada en Base64 de lienzo a 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);
Jugué con esto hace dos semanas, es muy simple. El único problema es que todos los tutoriales solo hablan sobre cómo guardar la imagen localmente. Así es como lo hice:
1) Configuré un formulario para poder usar un método POST.
2) Cuando el usuario termina de dibujar, puede hacer clic en el botón "Guardar".
3) Cuando se hace clic en el botón, tomo los datos de la imagen y los coloco en un campo oculto. Después de eso presento el formulario.
document.getElementById(''my_hidden'').value = canvas.toDataURL(''image/png'');
document.forms["form1"].submit();
4) Cuando se envía el formulario, tengo este pequeño 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'']);
?>
Si desea guardar los datos que se derivan de una función de canvas.toDataURL()
Javascript, debe convertir los espacios en blanco en plusses. Si no lo hace, los datos decodificados están dañados:
<?php
$encodedData = str_replace('' '',''+'',$encodedData);
$decocedData = base64_decode($encodedData);
?>