HTML5 Canvas'ı sunucuya görüntü olarak nasıl kaydederim?


261

Kullanıcıların ortaya çıkan görüntüleri bir algoritmadan kaydetmelerine izin vermek istediğim, üretken bir sanat projesi üzerinde çalışıyorum. Genel fikir:

  • Üretken bir algoritma kullanarak HTML5 Canvas'ta görüntü oluşturma
  • Görüntü tamamlandığında, kullanıcıların tuvali bir görüntü dosyası olarak sunucuya kaydetmesine izin verin
  • Kullanıcının görüntüyü indirmesine veya algoritmayı kullanarak oluşturulan parça galerisine eklemesine izin verin.

Ancak, ikinci adımda takıldım. Google'dan bazı yardımlardan sonra, tam olarak istediğim gibi görünen bu blog gönderisini buldum :

JavaScript koduna neden olan:

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

ve karşılık gelen 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);
}
?>

Ama bu hiçbir şey yapmıyor gibi görünüyor.

Daha fazla googling önceki öğretici dayalı bu blog yazısı açar . Çok farklı değil, ama belki de denemeye değer:

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

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

Bu bir dosya (yay) oluşturur, ancak bozuk ve hiçbir şey içermiyor gibi görünüyor. Ayrıca boş görünüyor (dosya boyutu 0).

Yanlış yaptığımı gerçekten açık bir şey var mı? Dosyamı sakladığım yol yazılabilir, bu bir sorun değil, ama hiçbir şey olmuyor gibi görünüyor ve bu nasıl hata ayıklamak gerçekten emin değilim.

Düzenle

Salvidor Dali'nin bağlantısını takiben AJAX talebini şu şekilde değiştirdim:

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

Ve şimdi görüntü dosyası yaratıldı ve boş değil! İçerik türü önemliydi ve x-www-form-urlencodedgörüntü verilerinin gönderilmesine izin verecek şekilde değiştirildiği anlaşılıyor .

Konsol, base64 kodunun (oldukça büyük) dizesini döndürür ve veri dosyası ~ 140 kB'dir. Ancak, hala açamıyorum ve bir görüntü olarak biçimlendirilmemiş gibi görünüyor.


İlk blog sağlanan kullanımları sonrası ajax.send(canvasData );sen gibi kullanırken ajax.send("imgData="+canvasData);. Bu nedenle $GLOBALS["HTTP_RAW_POST_DATA"]beklediğiniz gibi olmayacak, muhtemelen kullanmalısınız $_POST['imgData'].
Matteus Hemström


Diodeus: Zaten o iş parçacığında önerilen tekniği kullanıyorum; ancak, uygulama hakkında daha fazla ayrıntı veremediler ve bu noktada takıldım.
nathan lachenmyer

Ben dosya bilgileri yankı ( $dataikinci php kodunda) tüm döndüğüm boş bir satırdır. Neden olsun ki? Görünüşe göre gönderilen veriler doğru değil gibi görünüyor, ancak örneklerin gösterdiği gibi gönderiyorum ...
nathan lachenmyer

PHP kodu , bileşeni ile birlikte PHP-FileUpload kullanılarak basitleştirilebilir ve daha sağlam hale getirilebilir DataUriUpload. Burada belgelenmiştir ve güvenliği onaylamak ve uygulamak için birkaç ek yolla birlikte gelir.
Ocak

Yanıtlar:


242

İhtiyacınız olanı nasıl elde edeceğinize dair bir örnek:

1) Bir şey çizin ( tuval öğreticisinden alınır )

<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) Tuval resmini URL formatına dönüştürün (base64)

var dataURL = canvas.toDataURL();

3) Ajax üzerinden sunucunuza gönderin

$.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'i bir görüntü olarak sunucunuza kaydedin (işte bunu PHP'de nasıl yapacağınız , aynı fikirler her dilde. PHP'de sunucu tarafı burada bulunabilir ):


1
Demek istediğim dosya orada, ancak indirirsem tarayıcımda veya herhangi bir resim görüntüleyicide açılmıyor.
nathan lachenmyer

You send it to your server as an ajax requestbu yöntem kullanıcının onaylamasını gerektirir mi? Ya da tuvalden sunucuya sessizce görüntü gönderebilir miyim?
MyTitle

Web Konsolu'nda bir hata alıyorum. [16: 53: 43.227] SecurityError: İşlem güvenli değil. @ sharevi.com/staging/canvas.html:43 Bu bağlantı güvenli değil. Yapılması gereken bir şey var mı? /// GÜNCELLEME Sanırım nedenini biliyorum,
alanlar

1
ancak alfa 0'dır, bu da dolgusuz yapar, bu tamamen saydamdır. ilk üç değer ne olursa olsun.
Abel D

68

Bununla iki hafta önce oynadım, çok basit. Tek sorun, tüm öğreticiler sadece görüntüyü yerel olarak kaydetmek hakkında konuşmak. Ben böyle yaptım:

1) Bir POST yöntemi kullanabilmem için bir form oluşturdum.

2) Kullanıcı çizim yapmayı bitirdiğinde "Kaydet" düğmesini tıklayabilir.

3) Düğme tıklandığında görüntü verilerini alıp gizli bir alana koyarım. Bundan sonra formu gönderirim.

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

4) Form gönderildiğinde bu küçük php betiği var:

<?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 () kullanmam doğru olur mu? Jpeg, png, gif gibi dinamik dosya uzantılarım var. Canvas.toDataURL ('image / jpg') denedim ama çalışmıyor. Sorun nedir?
Ivo San

AJAX tarafından tuval resim göndermek çalışırken 413 (istek çok büyük) hatası alıyordum. Sonra bu yaklaşım görüntüleri aktarmamı sağladı.
Raiyan

1
Bunun bana 0kb png dosyası vermesinin herhangi bir nedeni var mı?
prismspecs

3
Jpg için canvas.toDataURL ('image / jpeg') yapmalısınız - en azından Chrome'da benim için ne yaptı.
Chris

Teşekkür ederim, ihtiyacım olan şey bu: D
FosAvance

22

Base64 görüntüsünü blob ile görüntüye aktarmanız gerektiğini düşünüyorum, çünkü base64 görüntüsünü kullandığınızda, çok sayıda günlük satırı alır veya sunucuya çok sayıda satır gönderir. Blob ile, sadece dosya. Aşağıdaki kodu kullanabilirsiniz:

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

Ve tuval kodu burada:

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

Bundan sonra Form ile ajax kullanabilirsiniz:

  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

Bu kod CoffeeScript sözdizimini kullanır.

javascript kullanmak istiyorsanız, lütfen kodu http://js2.coffee adresine yapıştırın.


12

Tuval resmini PHP'ye gönder:

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

İşte PHP betiği:
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

Bir Javascript canvas.toDataURL()işlevinden türetilen verileri kaydetmek istiyorsanız, boşlukları artılara dönüştürmeniz gerekir. Bunu yapmazsanız, kodu çözülen veriler bozulur:

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

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


7

Benzer bir şey üzerinde çalıştım. Tuval Base64 kodlu görüntüyü dönüştürmek zorunda kaldı 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

Salvador Dali'nin cevabına ek olarak:

sunucu tarafında verilerin base64 dize biçiminde geldiğini unutmayın . Bazı programlama dillerinde bu dizenin basit Unicode dizesi değil bayt olarak kabul edilmesi gerektiğini açıkça söylemeniz gerekir .

Aksi takdirde kod çözme çalışmaz: görüntü kaydedilir ancak okunamayan bir dosya olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.