Bagaimana cara menyimpan Kanvas HTML5 sebagai gambar di server?

261

Saya sedang mengerjakan proyek seni generatif di mana saya ingin memungkinkan pengguna untuk menyimpan gambar yang dihasilkan dari suatu algoritma. Ide umumnya adalah:

  • Buat gambar pada Kanvas HTML5 menggunakan algoritma generatif
  • Ketika gambar selesai, izinkan pengguna untuk menyimpan kanvas sebagai file gambar ke server
  • Izinkan pengguna mengunduh gambar atau menambahkannya ke galeri potongan-potongan yang dihasilkan menggunakan algoritma.

Namun, saya terjebak pada langkah kedua. Setelah bantuan dari Google, saya menemukan posting blog ini , yang tampaknya persis seperti yang saya inginkan:

Yang mengarah ke kode 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);
}

dan PHP yang sesuai (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);
}
?>

Tapi ini sepertinya tidak melakukan apa-apa sama sekali.

Lebih banyak Googling membuka posting blog ini yang didasarkan pada tutorial sebelumnya. Tidak jauh berbeda, tapi mungkin patut dicoba:

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

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

Yang ini membuat file (yay) tetapi rusak dan sepertinya tidak mengandung apa-apa. Tampaknya juga kosong (ukuran file 0).

Adakah sesuatu yang benar-benar jelas saya lakukan salah? Jalur tempat saya menyimpan file saya dapat ditulisi, jadi itu bukan masalah, tapi sepertinya tidak ada yang terjadi dan saya tidak begitu yakin bagaimana cara men-debug ini.

Edit

Mengikuti tautan Salvidor Dali, saya mengubah permintaan AJAX menjadi:

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

Dan sekarang file gambar dibuat dan tidak kosong! Sepertinya jenis konten yang penting dan yang mengubahnyax-www-form-urlencoded memungkinkan data gambar untuk dikirim.

Konsol mengembalikan string kode base64 (agak besar) dan datafile ~ 140 kB. Namun, saya masih tidak bisa membukanya dan sepertinya tidak diformat sebagai gambar.

nathan lachenmyer
sumber
Posting blog pertama yang Anda gunakan menggunakan ajax.send(canvasData );sementara Anda menggunakannya suka ajax.send("imgData="+canvasData);. Karena itu $GLOBALS["HTTP_RAW_POST_DATA"]tidak akan seperti yang Anda harapkan, mungkin sebaiknya Anda gunakan $_POST['imgData'].
Matteus Hemström
stackoverflow.com/questions/3592164/…
Diodeus - James MacFarlane
Diodeus: Saya sudah menggunakan teknik yang disarankan di utas itu; Namun, mereka gagal memberikan rincian lebih lanjut tentang implementasi dan di situlah saya terjebak.
nathan lachenmyer
Ketika saya mengulang informasi file ( $datadalam kode php kedua) semua yang saya dapatkan adalah baris kosong. Kenapa bisa begitu? Sepertinya mungkin data yang dikirim tidak benar, tetapi tampaknya saya mengirimkannya seperti contoh yang ditunjukkan ...
nathan lachenmyer
Kode PHP dapat disederhanakan dan dibuat lebih kuat dengan menggunakan PHP-FileUpload dengan DataUriUploadkomponennya sebagai gantinya. Itu didokumentasikan di sini dan dilengkapi dengan beberapa cara tambahan untuk validasi dan menegakkan keamanan.
gak

Jawaban:

242

Berikut adalah contoh cara mencapai apa yang Anda butuhkan:

1) Gambar sesuatu (diambil dari tutorial kanvas )

<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) Konversi gambar kanvas ke format URL (base64)

var dataURL = canvas.toDataURL();

3) Kirim ke server Anda melalui 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) Simpan base64 di server Anda sebagai gambar (di sini adalah cara melakukannya di PHP , ide yang sama ada di setiap bahasa. Sisi server di PHP dapat ditemukan di sini ):

Salvador Dali
sumber
1
Maksud saya, file itu ada di sana tetapi tidak terbuka di browser saya atau penampil gambar apa pun jika saya mengunduhnya.
nathan lachenmyer
You send it to your server as an ajax requestapakah metode ini memerlukan konfirmasi dari pengguna? Atau saya dapat mengirim gambar dari kanvas ke server secara diam-diam?
MyTitle
Saya mendapatkan kesalahan di Web Console. [16: 53: 43.227] SecurityError: Operasi tidak aman. @ sharevi.com/staging/canvas.html:43 koneksi ini tidak aman. Apakah ada sesuatu yang perlu dilakukan? /// UPDATE Saya pikir saya tahu mengapa, saya menggunakan gambar lintas domain
Nikolaos Vassos
1
tetapi alpha adalah 0 yang membuatnya tanpa isi, yang sepenuhnya transparan. tidak peduli apa nilai tiga yang pertama.
Abel D
68

Saya bermain dengan ini dua minggu lalu, sangat sederhana. Satu-satunya masalah adalah bahwa semua tutorial hanya berbicara tentang menyimpan gambar secara lokal. Beginilah cara saya melakukannya:

1) Saya mengatur formulir sehingga saya bisa menggunakan metode POST.

2) Ketika pengguna selesai menggambar, ia dapat mengklik tombol "Simpan".

3) Ketika tombol diklik saya mengambil data gambar dan memasukkannya ke dalam bidang tersembunyi. Setelah itu saya kirimkan form.

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

4) Ketika formulir dikirimkan saya punya skrip php kecil ini:

<?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']);
?>
pengguna568021
sumber
1
apakah boleh jika saya menggunakan canvas.toDataURL ()? Saya memiliki ekstensi file dinamis, seperti jpeg, png, gif. Saya mencoba canvas.toDataURL ('image / jpg') tetapi tidak berfungsi. Apa yang salah?
Ivo San
Saya mendapatkan kesalahan 413 (permintaan terlalu besar) mencoba mengirim gambar dari kanvas oleh AJAX. Kemudian pendekatan ini membantu saya mendapatkan gambar.
Raiyan
1
Ada alasan mengapa ini memberi saya file png 0kb?
prismspecs
3
Untuk jpg Anda perlu melakukan canvas.toDataURL ('image / jpeg') - setidaknya itulah yang melakukannya untuk saya di Chrome.
Chris
Terima kasih, itulah yang saya butuhkan: D
FosAvance
21

Saya pikir Anda harus mentransfer gambar di base64 ke gambar dengan gumpalan, karena ketika Anda menggunakan gambar base64, dibutuhkan banyak baris log atau banyak baris akan dikirim ke server. Dengan gumpalan, itu hanya file. Anda dapat menggunakan kode ini di bawah:

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

Dan kode kanvas di sini:

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

Setelah itu Anda bisa menggunakan ajax dengan 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

Kode ini menggunakan sintaks CoffeeScript.

jika Anda ingin menggunakan javascript, silakan tempel kode ke http://js2.coffee

ThienSuBS
sumber
12

Kirim gambar kanvas ke PHP:

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

Berikut skrip 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;
?>
vikram jeet singh
sumber
9

Jika Anda ingin menyimpan data yang berasal dari canvas.toDataURL()fungsi Javascript , Anda harus mengonversi kosong menjadi plusses. Jika Anda tidak melakukan itu, data yang didekodekan rusak:

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

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

MaloMax
sumber
7

Saya telah mengerjakan sesuatu yang serupa. Harus mengubah gambar yang disandikan kanvas Base64 ke 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.
sumber
4

Selain jawaban Salvador Dali:

di sisi server jangan lupa bahwa data datang dalam format string base64 . Ini penting karena dalam beberapa bahasa pemrograman Anda perlu secara eksplisit mengatakan bahwa string ini harus dianggap sebagai byte bukan string Unicode sederhana.

Kalau tidak, decoding tidak akan berfungsi: gambar akan disimpan tetapi itu akan menjadi file yang tidak dapat dibaca.

Ardine
sumber