Как сохранить PNG-изображение на стороне сервера, из строки данных base64
Я использую инструмент Canvas2Image от Nihilogic для преобразования рисунков холста в изображения PNG. Теперь мне нужно превратить те строки base64, которые генерирует этот инструмент, в настоящие файлы PNG на сервере, используя PHP.
Короче говоря, сейчас я создаю файл на стороне клиента с помощью Canvas2Image, затем извлекаю данные в кодировке base64 и отправляю их на сервер с помощью AJAX:
// Generate the image file
var image = Canvas2Image.saveAsPNG(canvas, true);
image.id = "canvasimage";
canvas.parentNode.replaceChild(image, canvas);
var url = 'hidden.php',
data = $('#canvasimage').attr('src');
$.ajax({
type: "POST",
url: url,
dataType: 'text',
data: {
base64data : data
}
});
В этот момент "hidden.php" получает блок данных, который выглядит как данные:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABE...
С этого момента я в значительной степени озадачен. Из того, что я прочитал, я считаю, что я должен использовать PHP-функцию imagecreatefromstring, но я не уверен, как на самом деле создать фактическое изображение PNG из строки в кодировке base64 и сохранить его на моем сервере. Пожалуйста, помогите!
17 ответов
Вам нужно извлечь данные изображения base64 из этой строки, декодировать их, а затем сохранить их на диск, GD вам не нужен, поскольку он уже является png.
$data = 'data:image/png;base64,AAAFBfj42Pj4';
list($type, $data) = explode(';', $data);
list(, $data) = explode(',', $data);
$data = base64_decode($data);
file_put_contents('/tmp/image.png', $data);
И как однострочник:
$data = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $data));
Эффективный метод извлечения, декодирования и проверки ошибок:
if (preg_match('/^data:image\/(\w+);base64,/', $data, $type)) {
$data = substr($data, strpos($data, ',') + 1);
$type = strtolower($type[1]); // jpg, png, gif
if (!in_array($type, [ 'jpg', 'jpeg', 'gif', 'png' ])) {
throw new \Exception('invalid image type');
}
$data = base64_decode($data);
if ($data === false) {
throw new \Exception('base64_decode failed');
}
} else {
throw new \Exception('did not match data URI with image data');
}
file_put_contents("img.{$type}", $data);
Я должен был заменить пробелы с плюсом str_replace(' ', '+', $img);
чтобы это заработало.
Вот полный код
$img = $_POST['img']; // Your data 'data:image/png;base64,AAAFBfj42Pj4';
$img = str_replace('data:image/png;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$data = base64_decode($img);
file_put_contents('/tmp/image.png', $data);
Надеюсь, это поможет.
Стоит сказать, что обсуждаемая тема задокументирована в RFC 2397 - Схема URL "данных" ( https://tools.ietf.org/html/rfc2397).
Из-за этого PHP имеет собственный способ обработки таких данных - "data: stream wrapper" ( http://php.net/manual/en/wrappers.data.php)
Таким образом, вы можете легко манипулировать своими данными с потоками PHP:
$data = 'data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7';
$source = fopen($data, 'r');
$destination = fopen('image.gif', 'w');
stream_copy_to_stream($source, $destination);
fclose($source);
fclose($destination);
Приняв идею @dre010, я расширил ее до другой функции, которая работает с любым типом изображения: PNG, JPG, JPEG или GIF и дает уникальное имя имени файла
Функция разделения данных изображения и типа изображения
function base64ToImage($imageData){
$data = 'data:image/png;base64,AAAFBfj42Pj4';
list($type, $imageData) = explode(';', $imageData);
list(,$extension) = explode('/',$type);
list(,$imageData) = explode(',', $imageData);
$fileName = uniqid().'.'.$extension;
$imageData = base64_decode($imageData);
file_put_contents($fileName, $imageData);
}
Ну, ваше решение выше зависит от изображения, являющегося файлом JPEG. Для общего решения я использовал
$img = $_POST['image'];
$img = substr(explode(";",$img)[1], 7);
file_put_contents('img.png', base64_decode($img));
PHP уже имеет справедливое отношение к base64 -> преобразование файла
Я использую, чтобы сделать это следующим образом:
$blob=$_POST['blob']; // base64 coming from an url, for example
$blob=file_get_contents($blob);
$fh=fopen("myfile.png",'w'); // be aware, it'll overwrite!
fwrite($fh,$blob);
fclose($fh);
echo '<img src=myfile.png>'; // just for the check
Всего проблем:
$data = 'data:image/png;base64,AAAFBfj42Pj4';
// Extract base64 file for standard data
$fileBin = file_get_contents($data);
$mimeType = mime_content_type($data);
// Check allowed mime type
if ('image/png'==$mimeType) {
file_put_contents('name.png', $fileBin);
}
Этот код работает для меня, проверьте код ниже:
<?php
define('UPLOAD_DIR', 'images/');
$image_parts = explode(";base64,", $_POST['image']);
$image_type_aux = explode("image/", $image_parts[0]);
$image_type = $image_type_aux[1];
$image_base64 = base64_decode($image_parts[1]);
$file = UPLOAD_DIR . uniqid() . '.png';
file_put_contents($file, $image_base64);
?>
Однолинейное решение.
$base64string = 'data:image/png;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7';
file_put_contents('img.png', base64_decode(explode(',',$base64string)[1]));
На основе примера drew010 я сделал рабочий пример для легкого понимания.
imagesaver("data:image/jpeg;base64,/9j/4AAQSkZJ"); //use full base64 data
function imagesaver($image_data){
list($type, $data) = explode(';', $image_data); // exploding data for later checking and validating
if (preg_match('/^data:image\/(\w+);base64,/', $image_data, $type)) {
$data = substr($data, strpos($data, ',') + 1);
$type = strtolower($type[1]); // jpg, png, gif
if (!in_array($type, [ 'jpg', 'jpeg', 'gif', 'png' ])) {
throw new \Exception('invalid image type');
}
$data = base64_decode($data);
if ($data === false) {
throw new \Exception('base64_decode failed');
}
} else {
throw new \Exception('did not match data URI with image data');
}
$fullname = time().$type;
if(file_put_contents($fullname, $data)){
$result = $fullname;
}else{
$result = "error";
}
/* it will return image name if image is saved successfully
or it will return error on failing to save image. */
return $result;
}
Попробуй это...
$file = $_POST['file']; //your data in base64 'data:image/png....';
$img = str_replace('data:image/png;base64,', '', $file);
file_put_contents('img/imag.png', base64_decode($img));
Предполагая, что у вас есть имя файла в $filename и ваша строка в кодировке base64 в $testfile, мой oneliner:
Эта функция должна работать. у него есть параметр photo, который содержит строку base64, а также путь к существующему каталогу изображений, если у вас уже есть существующее изображение, которое вы хотите удалить, пока вы сохраняете новое.
public function convertBase64ToImage($photo = null, $path = null) {
if (!empty($photo)) {
$photo = str_replace('data:image/png;base64,', '', $photo);
$photo = str_replace(' ', '+', $photo);
$photo = str_replace('data:image/jpeg;base64,', '', $photo);
$photo = str_replace('data:image/gif;base64,', '', $photo);
$entry = base64_decode($photo);
$image = imagecreatefromstring($entry);
$fileName = time() . ".jpeg";
$directory = "uploads/customer/" . $fileName;
header('Content-type:image/jpeg');
if (!empty($path)) {
if (file_exists($path)) {
unlink($path);
}
}
$saveImage = imagejpeg($image, $directory);
imagedestroy($image);
if ($saveImage) {
return $fileName;
} else {
return false; // image not saved
}
}
}
Это просто:
Давайте представим, что вы пытаетесь загрузить файл в рамках js, ajax-запроса или мобильного приложения (на стороне клиента).
- Сначала вы отправляете атрибут данных, который содержит строку в кодировке base64.
- На стороне сервера вы должны декодировать его и сохранить в локальной папке проекта.
Вот как это сделать с помощью PHP
<?php
$base64String = "kfezyufgzefhzefjizjfzfzefzefhuze"; // I put a static base64 string, you can implement you special code to retrieve the data received via the request.
$filePath = "/MyProject/public/uploads/img/test.png";
file_put_contents($filePath, base64_decode($base64String));
?>
Я надеюсь, что это поможет вам. Я решил эту проблему с помощью основного метода php. Мой решенный код, как показано ниже.
$base64string = 'BASE64 STRING GOES HERE';
$uploadpath = 'YOUR UPLOAD DIR PATH';
$parts = explode(";base64,", $base64string); //THIS WILL GET THE ORIGINAL FILE ENCODE STRING
$imagebase64 = base64_decode($parts[1]); //THIS WILL GET THE DECODED IMAGE STRING
$file = $uploadpath . uniqid() . '.png'; // THIS WILL GIVE THE FILE NAME AND SET THE FILE PATH
file_put_contents($file, $imagebase64); // THIS FUNCTION WILL STORE THE IMAGE TO GIVEN PATH WITH FILE_NAME
Если вы хотите случайным образом переименовать изображения и сохранить как путь к изображению в базе данных как blob, так и само изображение в папках, это решение поможет вам. Пользователи вашего веб-сайта могут хранить столько изображений, сколько захотят, при этом изображения будут случайным образом переименованы в целях безопасности.
PHP код
Генерация случайных символов varchars для использования в качестве имени изображения.
function genhash($strlen) {
$h_len = $len;
$cstrong = TRUE;
$sslkey = openssl_random_pseudo_bytes($h_len, $cstrong);
return bin2hex($sslkey);
}
$randName = genhash(3);
#You can increase or decrease length of the image name (1, 2, 3 or more).
Получить расширение данных изображения и часть base_64 (часть после данных:image/png;base64,) из изображения.
$pos = strpos($base64_img, ';');
$imgExten = explode('/', substr($base64_img, 0, $pos))[1];
$extens = ['jpg', 'jpe', 'jpeg', 'jfif', 'png', 'bmp', 'dib', 'gif' ];
if(in_array($imgExten, $extens)) {
$imgNewName = $randName. '.' . $imgExten;
$filepath = "resources/images/govdoc/".$imgNewName;
$fileP = fopen($filepath, 'wb');
$imgCont = explode(',', $base64_img);
fwrite($fileP, base64_decode($imgCont[1]));
fclose($fileP);
}
# => $filepath <= This path will be stored as blob type in database.
# base64_decoded images will be written in folder too.
# Please don't forget to up vote if you like my solution. :)