Làm cách nào để lưu Canvas HTML5 dưới dạng hình ảnh trên máy chủ?


261

Tôi đang làm việc trong một dự án nghệ thuật tổng quát nơi tôi muốn cho phép người dùng lưu hình ảnh thu được từ một thuật toán. Ý tưởng chung là:

  • Tạo hình ảnh trên Canvas HTML5 bằng thuật toán tổng quát
  • Khi hình ảnh hoàn thành, cho phép người dùng lưu canvas dưới dạng tệp hình ảnh vào máy chủ
  • Cho phép người dùng tải xuống hình ảnh hoặc thêm nó vào một bộ sưu tập các mảnh được sản xuất bằng thuật toán.

Tuy nhiên, tôi bị kẹt ở bước thứ hai. Sau một số trợ giúp từ Google, tôi tìm thấy bài đăng trên blog này , dường như chính xác những gì tôi muốn:

Điều này dẫn đến mã 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);
}

và PHP tương ứng (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);
}
?>

Nhưng điều này dường như không làm gì cả.

Nhiều Googling bật lên bài đăng blog này dựa trên hướng dẫn trước. Không khác lắm, nhưng có lẽ đáng để thử:

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

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

Cái này tạo ra một tập tin (yay) nhưng nó bị hỏng và dường như không chứa bất cứ thứ gì. Nó cũng có vẻ trống rỗng (kích thước tệp bằng 0).

Có điều gì thực sự rõ ràng rằng tôi đang làm sai? Đường dẫn nơi tôi lưu trữ tệp của mình có thể ghi được, vì vậy đó không phải là vấn đề, nhưng dường như không có gì xảy ra và tôi không thực sự chắc chắn làm thế nào để gỡ lỗi này.

Biên tập

Theo liên kết của Salvidor Dali, tôi đã thay đổi yêu cầu AJAX thành:

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

Và bây giờ tệp hình ảnh được tạo và không trống! Có vẻ như loại nội dung quan trọng và việc thay đổi nó để x-www-form-urlencodedcho phép dữ liệu hình ảnh được gửi.

Bảng điều khiển trả về chuỗi (khá lớn) của mã base64 và tệp dữ liệu là ~ 140 kB. Tuy nhiên, tôi vẫn không thể mở nó và dường như nó không được định dạng như một hình ảnh.


Blog đầu tiên đăng bài sử dụng của ajax.send(canvasData );bạn trong khi bạn sử dụng nó như thế nào ajax.send("imgData="+canvasData);. Do đó $GLOBALS["HTTP_RAW_POST_DATA"]sẽ không phải là những gì bạn mong đợi, có lẽ bạn nên sử dụng $_POST['imgData'].
Matteus Hemström


Diodeus: Tôi đã sử dụng kỹ thuật được đề xuất trong luồng đó; tuy nhiên, họ không cung cấp thêm thông tin chi tiết nào về việc triển khai và đó là nơi tôi đang gặp khó khăn.
nathan lachenmyer

Khi tôi lặp lại thông tin tệp ( $datatrong mã php thứ hai), tất cả những gì tôi nhận được là một dòng trống. Tại sao lại như vậy? Có vẻ như có lẽ dữ liệu được gửi không chính xác, nhưng có vẻ như tôi đang gửi nó giống như các ví dụ hiển thị ...
nathan lachenmyer

Mã PHP có thể được đơn giản hóa và làm cho mạnh mẽ hơn bằng cách sử dụng PHP-FileUpload với DataUriUploadthành phần của nó thay thế. Nó được ghi lại ở đây và đi kèm với một số phương tiện xác nhận bổ sung và thực thi bảo mật.
caw

Câu trả lời:


242

Dưới đây là một ví dụ về cách đạt được những gì bạn cần:

1) Vẽ một cái gì đó (lấy từ hướng dẫn canvas )

<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) Chuyển đổi hình ảnh canvas sang định dạng URL (base64)

var dataURL = canvas.toDataURL();

3) Gửi nó đến máy chủ của bạn thông qua 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) Lưu base64 trên máy chủ của bạn dưới dạng hình ảnh (đây là cách thực hiện điều này trong PHP , cùng một ý tưởng có trong mọi ngôn ngữ. Phía máy chủ trong PHP có thể được tìm thấy ở đây ):


1
Ý tôi là tập tin ở đó nhưng nó không mở trong trình duyệt của tôi hoặc bất kỳ trình xem ảnh nào nếu tôi tải xuống.
nathan lachenmyer

You send it to your server as an ajax requestphương pháp này có yêu cầu xác nhận của người dùng không? Hoặc tôi có thể âm thầm gửi hình ảnh từ vải đến máy chủ?
MyTitle

Tôi gặp lỗi trên Web Console. [16: 53: 43.227] SecurityError: Hoạt động không an toàn. @ sharevi.com/staging/canvas.html:43 kết nối này không an toàn. Có điều gì đó cần phải được thực hiện không? /// CẬP NHẬT Tôi nghĩ rằng tôi biết tại sao, tôi đã sử dụng hình ảnh tên miền chéo
Nikolaos Vassos

1
nhưng alpha là 0 làm cho nó không điền, điều đó hoàn toàn trong suốt. không có vấn đề gì với ba giá trị đầu tiên
Abel D

68

Tôi đã chơi với nó hai tuần trước, nó rất đơn giản. Vấn đề duy nhất là tất cả các hướng dẫn chỉ nói về việc lưu hình ảnh cục bộ. Đây là cách tôi đã làm nó:

1) Tôi thiết lập một biểu mẫu để tôi có thể sử dụng phương thức POST.

2) Khi người dùng vẽ xong, anh ta có thể nhấp vào nút "Lưu".

3) Khi nhấp vào nút, tôi lấy dữ liệu hình ảnh và đặt nó vào một trường ẩn. Sau đó tôi nộp mẫu đơn.

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

4) Khi biểu mẫu được gửi, tôi có tập lệnh php nhỏ này:

<?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
tôi có thể sử dụng canvas.toDataURL () không? Tôi có phần mở rộng tập tin động, như jpeg, png, gif. Tôi đã thử canvas.toDataURL ('image / jpg') nhưng nó không hoạt động. Chuyện gì vậy?
Ivo San

Tôi đã nhận được lỗi 413 (yêu cầu quá lớn) khi cố gắng gửi hình ảnh từ canvas bằng AJAX. Sau đó, phương pháp này đã giúp tôi có được những hình ảnh thông qua.
Raiyan

1
Bất kỳ lý do này là cho tôi một tập tin png 0kb?
lăng kính

3
Đối với jpg, bạn sẽ cần phải thực hiện canvas.toDataURL ('image / jpeg') - ít nhất đó là những gì đã làm cho tôi trên Chrome.
Chris

Cảm ơn bạn, đó là những gì tôi cần: D
FosAvance

21

Tôi nghĩ bạn nên chuyển hình ảnh trong Base64 thành hình ảnh với blob, bởi vì khi bạn sử dụng hình ảnh base64, sẽ mất rất nhiều dòng nhật ký hoặc rất nhiều dòng sẽ gửi đến máy chủ. Với blob, nó chỉ là tập tin. Bạn có thể sử dụng mã này dưới đây:

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

Và mã vải ở đây:

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

Sau đó, bạn có thể sử dụng ajax với 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

Mã này sử dụng cú pháp CoffeeScript.

nếu bạn muốn sử dụng javascript, vui lòng dán mã vào http://js2.coffee


12

Gửi hình ảnh vải tới PHP:

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

Đây là đoạn 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

Nếu bạn muốn lưu dữ liệu có nguồn gốc từ một canvas.toDataURL()hàm Javascript , bạn phải chuyển đổi khoảng trống thành kìm. Nếu bạn không làm điều đó, dữ liệu được giải mã sẽ bị hỏng:

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

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


7

Tôi đã làm việc trên một cái gì đó tương tự. Phải chuyển đổi hình ảnh được mã hóa Base64 sang 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

Ngoài câu trả lời của Salvador Dali:

về phía máy chủ đừng quên rằng dữ liệu có định dạng chuỗi base64 . Điều này rất quan trọng vì trong một số ngôn ngữ lập trình, bạn cần phải nói một cách rõ ràng rằng chuỗi này nên được coi là byte không phải là chuỗi Unicode đơn giản.

Nếu không giải mã sẽ không hoạt động: hình ảnh sẽ được lưu nhưng nó sẽ là một tập tin không thể đọc được.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.