Como salvar uma imagem PNG do lado do servidor, a partir de uma sequência de dados base64

212

Estou usando a ferramenta JavaScript "Canvas2Image" da Nihilogic para converter desenhos de tela em imagens PNG. O que eu preciso agora é transformar as strings base64 que essa ferramenta gera, em arquivos PNG reais no servidor, usando PHP.

Em resumo, o que estou fazendo atualmente é gerar um arquivo no lado do cliente usando o Canvas2Image, recuperar os dados codificados em base64 e enviá-los ao servidor usando o 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
    }
});

Neste ponto, "hidden.php" recebe um bloco de dados parecido com dados: image / png; base64, iVBORw0KGgoAAAANSUhEUgAABE ...

A partir deste momento, estou praticamente perplexo. Pelo que li, acredito que devo usar a função imagecreatefromstring do PHP , mas não tenho certeza de como criar uma imagem PNG real a partir da string codificada em base64 e armazená-la no meu servidor. Por favor, ajudem!

Andrei Oniga
fonte
você precisa analisá-lo. você pode extrair tipo de imagem a partir de lá e, em seguida, usar base64_decode e salvar essa string em um arquivo por seu tipo de imagem
Constantin
@ Constantine Você pode ser mais específico, por favor?
Andrei Oniga 16/07/2012
7
$ data = $ _REQUEST ['base64data']; $ image = explodir ('base64,', $ dados); file_put_contents ('img.png', base64_decode ($ image [1]));
Constantin
você pode postar o código completo, desde o instantâneo e até enviar os dados, não está funcionando para mim.
Ofir Attia

Respostas:

437

Você precisa extrair os dados da imagem base64 dessa string, decodificá-los e salvá-los no disco, não precisa do GD, pois já é um 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);

E como uma linha:

$data = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $data));

Um método eficiente para extrair, decodificar e verificar erros é:

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);
drew010
fonte
Você tem $ type no seu exemplo. Qual seria esse valor?
11117 Jon
1
@Jon $typerecebe um valor atribuído da explosão, dependendo se são dados: imagem / jpg ou dados: imagem / png etc.
drew010
Eu recebi este erro: caracteres utf-8 malformados possivelmente codificados incorretamente, o que devo fazer?
aldo
@ aldo Provavelmente poste uma nova pergunta com código e detalhes. É praticamente impossível dizer com base apenas nessa mensagem e sem ver seu código ou saber como a imagem é codificada e como é enviada para o script.
precisa saber é o seguinte
120

Tente o seguinte:

file_put_contents('img.png', base64_decode($base64string));

file_put_contents docs

Um cara
fonte
4
Eu tentei isso, mas está incluindo o data:image/png;base64,que quebra o arquivo.
Michael J. Calkins
2
@MichaelCalkins Também quebra isso para mim. A resposta de drew010 parece a única solução que funciona de maneira consistente.
David John Welsh
24
Se você estiver incluindo a data:image/png;base64,sequência, a sequência para a qual está passando base64_decodenão é válida base64. Você precisa enviar apenas os dados, que é o que a resposta de drew010 está ilustrando. Não há nada quebrado com esta resposta. Você não pode copiar e colar sem entender e espera que "funcione".
Cypher
4
Não está funcionando para o exemplo fornecido na pergunta, que não é um conteúdo base64, e primeiro deve ser dividido em partes (consulte a resposta aceita).
Benjamin Piette
minha imagem foi salva com sucesso. mas quando escrevo url da imagem, vejo uma imagem em branco. Tenho certeza de que meu dataURL está correto, porque testei usando window.open (dataURL). Por que uma imagem em branco?
Partho
45

Eu tive que substituir espaços por mais símbolos str_replace(' ', '+', $img);para fazer isso funcionar.

Aqui está o código completo

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

Espero que ajude.

Ben
fonte
1
Sua linha $img = str_replace(' ', '+', $img);me ajudou muito, obrigado! Por que eu tenho que converter o espaço para "+" antes?
Sven
19

Vale dizer que o tópico discutido está documentado na RFC 2397 - O esquema de URL "dados" ( https://tools.ietf.org/html/rfc2397 )

Por causa disso, o PHP tem uma maneira nativa de lidar com esses dados - "data: stream wrapper" ( http://php.net/manual/en/wrappers.data.php )

Portanto, você pode manipular facilmente seus dados com fluxos 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);
Vladimir Posvistelik
fonte
2
Eu gosto dessa resposta, ela deveria ter mais votos, está usando funções nativas, sem manipulação de dados caseira suja! Embora, alguma idéia sobre performances? É, como se poderia esperar, mais rápido que preg_replace+ file_put_contents?
Bonswouar 18/10/19
1
@Bonswouar Thanks. Com relação à sua pergunta: na minha opinião, a melhor maneira de garantir que seu aplicativo não tenha problemas de desempenho seria medir seu desempenho no seu caso específico (com base no ambiente, tamanho de arquivo permitido e permitido, etc.). Poderíamos tentar brincar com o código aqui e mostrar os números na resposta, mas a verdade é que, no seu caso exato, isso poderia trazer mais danos do que resultados positivos. É sempre melhor ter certeza por conta própria (especialmente se estamos falando de partes críticas do desempenho do aplicativo).
Vladimir Posvistelik
11

Bem, sua solução acima depende da imagem ser um arquivo jpeg. Para uma solução geral, usei

$img = $_POST['image'];
$img = substr(explode(";",$img)[1], 7);
file_put_contents('img.png', base64_decode($img));
devil_io
fonte
11

Adotada a idéia @ dre010, estendi-a para outra função que funciona com qualquer tipo de imagem: PNG, JPG, JPEG ou GIF e fornece um nome exclusivo ao nome do arquivo

A função separa dados e tipo de imagem

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);
}
PolloZen
fonte
5

Solução linear.

$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]));
Piotr
fonte
5

com base no exemplo drew010, fiz um exemplo prático para facilitar o entendimento.

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; 
}
sanjeet bisht
fonte
4

tente isso ...

$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));
Paline
fonte
4

Este código funciona para mim, verifique o código abaixo:

<?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);
?>
gauravbhai daxini
fonte
0

Esta função deve funcionar. ele possui o parâmetro photo que contém a string base64 e também o caminho para um diretório de imagens existente, caso você já tenha uma imagem existente que deseja desvincular enquanto salva a nova.

 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
        }
    }
}
Salvador Dela
fonte
0

É simples :

Vamos imaginar que você esteja tentando fazer upload de um arquivo dentro da estrutura js, solicitação ajax ou aplicativo móvel (lado do cliente)

  1. Primeiro, você envia um atributo de dados que contém uma sequência codificada em base64.
  2. No lado do servidor, você deve decodificá-lo e salvá-lo em uma pasta local do projeto.

Aqui como fazê-lo usando 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));

?>
MUSTAPHA GHLISSI
fonte
0

Se você deseja renomear imagens aleatoriamente e armazenar o caminho da imagem no banco de dados como blob e a própria imagem nas pastas, esta solução o ajudará. Os usuários do seu site podem armazenar quantas imagens quiserem, enquanto as imagens serão renomeadas aleatoriamente por motivos de segurança.

Código php

Gere varchars aleatórios para usar como nome da imagem.

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

Obtenha a extensão de dados da imagem e a parte base_64 (parte após os dados: image / png; base64,) da imagem.

$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. :)
Chikeluba Anusionwu
fonte
0

PHP já tem um tratamento justo base64 -> transformação de arquivo

Eu uso para fazê-lo desta maneira:

$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
PYK
fonte