Search code examples
phpmysqlblobimagickphp-gd

How to extract possibly corrupt image blobs from a mysql database in PHP and save to files


I have a database table which contains receipt images, encoded into BLOB data in PHP. The table has the following columns and data:

id   | ownerid | file | filename            | size   | type       | fileerror
-----|---------|------|---------------------|--------|------------|-----------
1644 |       9 | BLOB | 2014-03-0714.30.jpg | 996379 | image/jpeg |      NULL

The file type was captured on original upload via a webform using one of either iMagick or GD, converted to a blob, and stored in the database.

The schema may have been converted from MyISAM to InnoDB at some point. The encoding may have been converted from Latin1 to UTF8-unicode-ci at some point. It may also have always been set thus.

I have subsequently tried all of the methods of converting the BLOB back to an image I can think of, however I never end up with an image.

I've tried using Imagick::readImageBlob($myBlob) and ended up with a big fat file which doesn't want to be an image, and I've tried using imagecreatefromstring($myBlob).

I'm using file_put_contents() to save the file.

I gather BLOBs are binary, and should stay binary, and the encoding should not effect them... however I cannot get my blobs to be pictures anymore.

How can I figure out if:

  1. My binary blobs are corrupted in the database, and I should give up.
  2. My binary blobs are fine thanks, and my PHP skills are failing me.
  3. Some kind of character encoding thing is happening behind the scenes in php I can fix.

I just tried saving the blob straight to a file from mysql workbench, and that didn't help either.

Edit: A little taste of blob (taken from the file I dumped, via chrome)

/9j/4FwwEEpGSUZcMAEBAVwwYFwwYFwwXDD/4Vww8kV4aWZcMFwwTU1cMCpcMFwwXDAIXDAMAVwwXDADXDBcMFwwAQmQXDBcMAEBXDADXDBcMFwwAQcsXDBcMAEPXDACXDBcMFwwBE

Solution

  • Based on the BLOB data you posted, the file magic number /9j/4 is jpeg;base64, so this should get part of the way there:

    $data = base64_decode($data)
    file_put_contents('2014-03-0714.30.jpg', $data);
    

    The data looks like a JPEG:

     ╪ α\0►JFIF\0☺☺☺\0`\0`\0\0 ß\0≥Exif\0\0MM\0*\0\0\\0♀☺\0\0♥\0\0\0☺       É\0\0☺☺\0♥\0\0\0☺,\0\0☺☼\0☻\    0\0\0♦
    

    However the slashes \ look suspect so the data may have been escaped before being encoded, so try:

    $data = stripslashes(base64_decode($data));
    

    Which looks better:

     ╪ α ►JFIF ☺☺☺ ` `   ß ≥Exif  MM *  ♀☺  ♥   ☺   É  ☺☺ ♥   ☺,  ☺☼ ☻   ♦
    

    And for comparison, similar to Wikipedia's Stonehenge:

     ╪ α ►JFIF ☺☻☺ x x   ß▬Exif   MM *  ☺↕  ♥   ☺☺ ☺→♣  ☺ b☺←♣ ☺j☺(♥ ☺☻ ☺1