Saving HTML canvas element data to an image in a user friendly manner is a tricky problem. Let’s look at one way to solve it.
First Attempt
We could always open our canvas in a new browser tab (or window) with the toDataURL
JavaScript method.
window.location.href = canvas.toDataURL("image/png");
Unfortunately this requires the user to use the file menu or the right-click button to save the image from the newly opened browser tab. I wouldn’t call this user friendly.
Second Attempt
After some investigation I came across Nihilogic’s Canvas2Image
JavaScript package. This package presents a Dialog Box to the user allowing them to save the image. This would solve my problem except the downloaded filename has the format 8iqALWM5.part . If my mom encountered a filename like that she wouldn’t know what to do with it. Still not user friendly.
Final Attempt
What to do? Enter PHP.
Permadi presents a technique using PHP and AJAX that is exactly what I need. After some tweaking here’s what I came up with.
save.php
The first PHP file saves the passed in canvas data to the server at a random location determined by the md5(uniqid()) method.
$data = $_POST['data'];
$file = md5(uniqid()) . '.png';
// remove "data:image/png;base64,"
$uri = substr($data,strpos($data,",") 1);
// save to file
file_put_contents($file, base64_decode($uri));
// return the filename
echo json_encode($file);
We would call this via JQuery with the $.post method, filling the data parameter using the toDataURL
method.
$.post("save.php", {data: canvas.toDataURL("image/png")})
download.php
Now we can use PHP to force the download of the saved image data. You can read more about this in the PHP Manual.
$file = trim($_GET['path']);
// force user to download the image
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: image/png');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
else {
echo "$file not found";
}
We can use the return value of our first AJAX request as input to download.php
to provide the filename.
$("#save").click(function () {
$.post("save.php", {data: G_cv.toDataURL("image/png")}, function (file) {
window.location.href = "download.php?path=" file});
});
Now when the Save
link is clicked a dialog box will be presented to the user asking them to save their image.
User friendly?