Jak zapisać obraz PNG po stronie serwera, z ciągu danych base64

212

Korzystam z narzędzia JavaScript „Canvas2Image” Nihilogic do konwersji rysunków na obrazy w formacie PNG. Teraz potrzebuję przekształcić ciągi base64 generowane przez to narzędzie w rzeczywiste pliki PNG na serwerze, używając PHP.

Krótko mówiąc, obecnie tworzę plik po stronie klienta za pomocą Canvas2Image, a następnie pobieram dane zakodowane w standardzie base64 i wysyłam na serwer za pomocą 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
    }
});

W tym momencie „hidden.php” otrzymuje blok danych, który wygląda jak data: image / png; base64, iVBORw0KGgoAAAANSUhEUgAABE ...

Od tego momentu jestem prawie zakłopotany. Z tego, co przeczytałem, uważam, że powinienem użyć funkcji imagecreatefromstring PHP , ale nie jestem pewien, jak faktycznie utworzyć rzeczywisty obraz PNG z ciągu zakodowanego w base64 i zapisać go na moim serwerze. Proszę o pomoc!

Andrei Oniga
źródło
musisz to przeanalizować. możesz wyodrębnić stamtąd typ obrazu, a następnie użyć kodu base64_decode i zapisać ten ciąg w pliku według typu obrazu
Constantin
@ Constantine Czy możesz być bardziej szczegółowy?
Andrei Oniga,
7
$ data = $ _REQUEST ['base64data']; $ image = eksplodować ('base64,', $ data); file_put_contents ('img.png', base64_decode ($ image [1]));
Constantin,
możesz opublikować pełny kod z migawki i dopóki nie wyślesz danych, to nie działa dla mnie.
Ofir Attia

Odpowiedzi:

437

Musisz wyodrębnić dane obrazu base64 z tego ciągu, zdekodować go, a następnie możesz zapisać go na dysku, nie potrzebujesz GD, ponieważ jest już 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);

I jako jedna linijka:

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

Wydajną metodą wyodrębniania, dekodowania i sprawdzania błędów jest:

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
źródło
W twoim przykładzie wpisałeś $. Jaka powinna być ta wartość?
Jon
1
@Jon $typeotrzymuje wartość z rozstrzelenia , w zależności od tego, czy są to dane: obraz / jpg, czy dane: obraz / png itp.
Draw010
dostałem ten błąd: zniekształcone znaki utf-8 prawdopodobnie nieprawidłowo zakodowane, co powinienem zrobić?
aldo
@aldo Prawdopodobnie opublikuj nowe pytanie z kodem i szczegółami. Niemożliwe do powiedzenia na podstawie tej wiadomości i bez obejrzenia kodu lub wiedzy o tym, jak obraz jest kodowany i jak jest wysyłany do skryptu.
drew010
120

Spróbuj tego:

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

file_put_contents docs

Jakiś facet
źródło
4
Próbowałem tego, ale zawiera to, data:image/png;base64,co psuje plik.
Michael J. Calkins,
2
@MichaelCalkins Też mnie łamie. Odpowiedź Draw010 wydaje się jedynym rozwiązaniem, które działa konsekwentnie.
David John Welsh
24
Jeśli dołączasz, data:image/png;base64,to ciąg, który przekazujesz, base64_decodenie jest prawidłowy base64. Musisz tylko wysłać dane, co ilustruje odpowiedź Draw010. Z tą odpowiedzią nic nie jest zepsute. Nie możesz kopiować i wklejać bez zrozumienia i oczekujesz, że „po prostu zadziała”.
Cypher
4
Nie działa w przykładzie podanym w pytaniu, który nie jest zawartością base64, i najpierw musi zostać podzielony na części (patrz zaakceptowana odpowiedź).
Benjamin Piette
mój obraz został pomyślnie zapisany. ale kiedy piszę adres URL obrazu, widzę pusty obraz. Jestem pewien, że moje daneURL są poprawne, ponieważ przetestowałem je przy użyciu window.open (dataURL). Dlaczego pusty obraz?
część
45

Musiałem zamienić spacje na symbole plus, str_replace(' ', '+', $img);aby to zadziałało.

Oto pełny kod

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

Mam nadzieję, że to pomaga.

Ben
źródło
1
Twoja linia $img = str_replace(' ', '+', $img);bardzo mi pomogła, dzięki! Dlaczego wcześniej muszę przekonwertować spację na „+”?
Sven
19

Warto powiedzieć, że omawiany temat jest udokumentowany w RFC 2397 - Schemat URL „danych” ( https://tools.ietf.org/html/rfc2397 )

Z tego powodu PHP ma natywny sposób obsługi takich danych - „data: stream wrapper” ( http://php.net/manual/en/wrappers.data.php )

Możesz więc łatwo manipulować danymi za pomocą strumieni 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
źródło
2
Podoba mi się ta odpowiedź, powinna mieć więcej głosów, korzysta z natywnych funkcji, nie ma brudnych domowych manipulacji danymi! Chociaż masz pomysł na występy? Czy to, jak można się spodziewać, szybciej niż preg_replace+ file_put_contents?
Bonswouar
1
@Bonswouar Thanks. Jeśli chodzi o twoje pytanie: moim zdaniem najlepszym sposobem upewnienia się, że twoja aplikacja nie ma problemów z wydajnością, jest zmierzenie jej wydajności w konkretnym przypadku (na podstawie środowiska, oczekiwanego i dozwolonego rozmiaru pliku itd.). Możemy spróbować bawić się kodem tutaj i wyświetlać liczby w odpowiedzi, ale prawda jest taka, że ​​w twoim konkretnym przypadku może przynieść więcej szkody niż pozytywny wynik. Zawsze lepiej jest upewnić się na własną rękę (zwłaszcza jeśli mówimy o częściach aplikacji o kluczowym znaczeniu dla wydajności).
Vladimir Posvistelik
11

Cóż, powyższe rozwiązanie zależy od tego, czy obraz jest plikiem JPEG. Jako ogólne rozwiązanie zastosowałem

$img = $_POST['image'];
$img = substr(explode(";",$img)[1], 7);
file_put_contents('img.png', base64_decode($img));
devil_io
źródło
11

W ramach pomysłu @ dre010 rozszerzyłem go na inną funkcję, która działa z dowolnym typem obrazu: PNG, JPG, JPEG lub GIF i nadaje unikalną nazwę nazwie pliku

Funkcja oddziela dane obrazu i typ obrazu

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
źródło
5

Rozwiązanie jednoliniowe.

$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
źródło
5

na podstawie przykładu Draw010 stworzyłem działający przykład dla łatwego zrozumienia.

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
źródło
4

Spróbuj tego...

$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
źródło
4

Ten kod działa dla mnie sprawdź poniższy kod:

<?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
źródło
0

Ta funkcja powinna działać. ma parametr foto, który przechowuje ciąg base64, a także ścieżkę do istniejącego katalogu obrazów, jeśli masz już obraz, który chcesz rozłączyć podczas zapisywania nowego.

 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
        }
    }
}
Saviour Dela
źródło
0

To proste :

Wyobraźmy sobie, że próbujesz przesłać plik w ramach js, żądania ajax lub aplikacji mobilnej (po stronie klienta)

  1. Najpierw wysyłasz atrybut danych zawierający ciąg zakodowany w standardzie base64.
  2. Po stronie serwera musisz go zdekodować i zapisać w lokalnym folderze projektu.

Tutaj, jak to zrobić za pomocą 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
źródło
0

Jeśli chcesz losowo zmieniać nazwy obrazów i przechowywać zarówno ścieżkę obrazu w bazie danych jako obiekt blob, jak i sam obraz w folderach, to rozwiązanie ci pomoże. Użytkownicy Twojej witryny mogą przechowywać tyle obrazów, ile chcą, podczas gdy zdjęcia będą losowo zmieniane ze względów bezpieczeństwa.

Kod php

Generuj losowe varchary do użycia jako nazwy obrazu.

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

Pobierz rozszerzenie danych obrazu i część base_64 (część po danych: obraz / png; base64,) z obrazu.

$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
źródło
0

PHP ma już uczciwe traktowanie base64 -> transformacja plików

Robię to w ten sposób:

$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
źródło