Як зберегти HTML5 Полотно як зображення на сервері?


260

Я працюю над генеративним арт-проектом, де я хотів би дозволити користувачам зберігати отримані зображення з алгоритму. Загальна ідея:

  • Створіть зображення на полотні HTML5 за допомогою генеративного алгоритму
  • Коли зображення завершено, дозвольте користувачам зберігати полотно як файл зображення на сервері
  • Дозвольте користувачу або завантажити зображення, або додати його в галерею фрагментів, створених за допомогою алгоритму.

Однак я застряг на другому кроці. Після деякої допомоги від Google я знайшов цю публікацію в блозі , яка, здавалося, саме те, що я хотів:

Що призвело до коду 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);
}

і відповідний 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);
}
?>

Але, схоже, це взагалі нічого не робить.

Більше Googling з'являється ця публікація в блозі, яка базується на попередньому навчальному посібнику. Не дуже різні, але, можливо, варто спробувати:

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

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

Цей створює файл (так), але він пошкоджений і, схоже, нічого не містить. Він також видається порожнім (розмір файлу 0).

Чи є щось насправді очевидне, що я роблю неправильно? Шлях, де я зберігаю свій файл, можна записати, тому це не проблема, але, здається, нічого не відбувається, і я не дуже впевнений, як це налагодити.

Редагувати

За посиланням Сальвідора Далі я змінив запит AJAX на:

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

А тепер файл зображення створений і не порожній! Схоже, що тип вмісту має значення і змінивши його на x-www-form-urlencodedдозвіл передавати дані зображення.

Консоль повертає (досить великий) рядок коду base64, а файл даних становить ~ 140 кБ. Однак я все ще не можу її відкрити, і, здається, вона не відформатована як зображення.


Перша публікація в блозі, яку ajax.send(canvasData );ви надаєте, поки ви не любите ajax.send("imgData="+canvasData);. Тому $GLOBALS["HTTP_RAW_POST_DATA"]не буде того, чого ви очікуєте, ви, ймовірно, повинні використовувати $_POST['imgData'].
Matteus Hemström


Діодей: Я вже використовую техніку, запропоновану в цій темі; однак вони не змогли надати більше детальних відомостей про реалізацію, і саме там я застрягаю.
nathan lachenmyer

Коли я повторюю інформацію про файл ( $dataу другому PHP-коді), все, що я повертаю, - це порожній рядок. Чому це було б? Здається, що, можливо, дані, що надсилаються,
невірні

PHP-код можна спростити і зробити більш надійним, використовуючи натомість PHP-FileUpload з його DataUriUploadкомпонентом. Він задокументований тут і постачається з кількома додатковими засобами перевірки та забезпечення безпеки.
каре

Відповіді:


241

Ось приклад, як досягти того, що вам потрібно:

1) Намалюйте щось (узяте з полотна підручника )

<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) Перетворення зображення полотна у формат URL (base64)

var dataURL = canvas.toDataURL();

3) Відправте його на свій сервер через 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) Збережіть base64 на своєму сервері як зображення (ось як це зробити в PHP , однакові ідеї є на кожній мові. Сторінку сервера в PHP можна знайти тут ):


1
Я маю на увазі, що файл є, але він не відкривається в моєму браузері чи будь-якому переглядачі зображень, якщо я його завантажую.
nathan lachenmyer

You send it to your server as an ajax requestчи потрібен цей метод підтвердження користувача? Або я мовчки можу надсилати зображення з полотна на сервер?
MyTitle

Я отримую помилку на веб-консолі. [16: 53: 43.227] SecurityError: Операція небезпечна. @ sharevi.com/staging/canvas.html:43 це з'єднання небезпечно. Чи потрібно щось зробити? /// ОНОВЛЕННЯ Я думаю, що я знаю чому, я використовував міждоменні зображення
Nikolaos Vassos

1
але альфа дорівнює 0, що робить його незаповненим, тобто повністю прозорим. незалежно від того, які перші три значення.
Abel D

68

Я грав із цим два тижні тому, це дуже просто. Єдина проблема полягає в тому, що всі підручники говорять лише про збереження зображення на локальному рівні. Ось як я це зробив:

1) Я створив форму, щоб я міг використовувати метод POST.

2) Коли користувач закінчить малювати, він може натиснути кнопку «Зберегти».

3) Після натискання кнопки я беру дані про зображення і розміщую їх у прихованому полі. Після цього я надсилаю форму.

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

4) Коли форма розміщена, у мене є цей невеликий скрипт для 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
це нормально, якщо я використовую canvas.toDataURL ()? У мене є динамічне розширення файлів, як jpeg, png, gif. Я спробував canvas.toDataURL ('image / jpg'), але він не працює. Що не так?
Іво Сан

Я отримував помилку 413 (запит занадто великий), намагаючись надіслати зображення з полотна AJAX. Тоді цей підхід допоміг мені перетворити образи.
Райян

1
Якась причина дала мені файл 0kb PNG?
prizmspecs

3
Для jpg вам потрібно зробити canvas.toDataURL ('image / jpeg') - принаймні, те, що було для мене в Chrome.
Кріс

Дякую, ось що мені було потрібно: D
FosAvance

21

Я думаю, вам слід перенести зображення в base64 на зображення з крапом, тому що, коли ви використовуєте зображення base64, потрібно багато ліній журналів, або багато рядків буде відправлено на сервер. З крапом це лише файл. Ви можете використовувати цей код нижче:

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

І код канви тут:

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

Після цього ви можете використовувати ajax з 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

Цей код за допомогою синтаксису CoffeeScript.

якщо ви хочете використовувати javascript, будь ласка, вставте код у http://js2.coffee


12

Надіслати зображення полотна на PHP:

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

Ось сценарій 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

Якщо ви хочете зберегти дані, отримані від canvas.toDataURL()функції Javascript , вам доведеться конвертувати заготовки в плюси. Якщо цього не зробити, декодовані дані пошкоджуються:

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

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


7

Я працював над чимось подібним. Довелося конвертувати зображення, закодоване Base64, у зображення 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

Окрім відповіді Сальвадора Далі:

на стороні сервера , не забувайте про те , що дані надходять в base64 рядки формату. Це важливо, оскільки в деяких мовах програмування потрібно чітко сказати, що цей рядок слід розглядати як байти не простий рядок Unicode.

Інакше розшифровка не буде працювати: зображення буде збережено, але це буде файл, який не можна прочитати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.