4

I am attempting to write to an image file from a blob.

 if($_POST['logoFilename'] != 'undefined'){
  $logoFile = fopen($_POST['logoFilename'], 'w') or die ("Cannot create ".$_POST['logoFilename']);

  fwrite($logoFile, $_POST['logoImage']);

  fclose($logoFile);
}

In the previous code snippet, $_POST['logoImage'] is a BLOB. The file is correctly written to the root directory, however the file cannot be opened. In ubuntu 11.04 I receive the following error:

Error interpreting JPEG image file (Not a JPEG file: starts with 0x64 0x61).

The BLOB does correctly display if I create an img and set its src=blob

Included below is the first snippet of the BLOB:

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAgGBgcGBQgHBwcJCQ
GAgnew
  • 3,847
  • 3
  • 26
  • 28

4 Answers4

10

Your "Blob" is really a Data URI:

data:[<MIME-type>][;charset=<encoding>][;base64],<data>

Since you only want the decoded data part, you have to do

file_put_contents(
    'image.jpg',
    base64_decode( 
        str_replace('data:image/jpeg;base64,', '', $blob)
    )
);

But since PHP natively supports data:// streams, you can also do (thanks @NikiC)

file_put_contents('image.jpg', file_get_contents($blob));

If the above doesnt work, you can try with GDlib:

imagejpg(
    imagecreatefromstring(
        base64_decode( 
            str_replace('data:image/jpeg;base64,', '', $blob)
        )
    ), 
    'image.jpg'
);
Gordon
  • 312,688
  • 75
  • 539
  • 559
  • @Greg Hmm, make sure the image wasnt corrupted during transfer. Does the $blog content end in `==`? – Gordon Jul 15 '11 at 18:35
  • It ends in '/9k=', however this is the same way it ends when being rendered properly by Javascript in an img object. – GAgnew Jul 15 '11 at 18:39
  • @Greg Is it confidential or can you put the data uri at http://pastebin.com? Would like to try if I get the same results. – Gordon Jul 15 '11 at 18:44
  • As a final note, the real problem is that I have to replace all blanks spaces with '+'s. – GAgnew Jul 18 '11 at 19:18
0

If it's a file upload control, $_POST won't contain the information. You're looking for handling file uploads with $_FILES. (And more specifically, move_uploaded_file)

Given the new update, try the following:

  // 
  // Export a image blob to a file using either the specific image name
  // @blob     : The actual image blob
  // @fileName : Can be the explicit name (with an extension) or this can be
  //             just a generic file name and the extension (based on data
  //             type) will be appended automatically. This can also include
  //             path information.
  // Exmaples:
  //   storeBlob('data:image/png;base64,...', 'myimage');      ::  saves as myimage.png
  //   storeBlob('data:image/jpg;base64,...', 'img/new.jpg');  ::  saves as img/new.jpg
  function storeBlob($blob, $fileName)
  {
    $blobRE = '/^data:((\w+)\/(\w+));base64,(.*)$/';
    if (preg_match($blobRE, $blob, $m))
    {
      $imageName = (preg_match('/\.\w{2,4}$/', $fileName) ? $fileName : sprintf('%s.%s', $fileName, $m[3]));

      return file_put_contents($imageName,base64_decode($m[4]));
    }
    return false; // error
  }
Brad Christie
  • 100,477
  • 16
  • 156
  • 200
  • It is from a fileUpload control, but it is not a form submit. It is submitted via AJAX as a raw string blob. – GAgnew Jul 15 '11 at 16:42
  • @Greg: To my knowledge, ajax doesn't support uploading a file. Sites typically use another tool (such as flash or activeX object) which DOES have the ability to upload binary data. However, I did see [an article on ajax file uploading](http://www.anyexample.com/programming/php/php_ajax_example__asynchronous_file_upload.xml) that may interest you. – Brad Christie Jul 15 '11 at 16:44
  • 1
    Agreed. Unless this data is coming from an unusual client (some flash thing, or maybe server-side code on another server that is calling this stuff as a web service), it seems very strange. There's no possible way to get data from a user's filesystem into a POST variable using a vanilla web browser. – timdev Jul 15 '11 at 16:47
  • @Timdev: agreed. And even if you could, the binary would be killed through "Traditional post methods", so either way you'd never get an image out of it (unless you could manage to base64 encode it before it hit the transfer) – Brad Christie Jul 15 '11 at 16:48
  • Well your knowledge could use some updating then. I'm sending a _BLOB_ (Its just a string of text.. (or binary depending on how you represent it)). I can transfer as much text as I want, I don't see any reason why a XMLHTTPRequest would change that data. – GAgnew Jul 15 '11 at 16:49
  • Okay. shit. well first let me teach you how to do that, and then maybe you can help me solve my actual problem instead of telling me what im ALREADY DOING is impossible. – GAgnew Jul 15 '11 at 16:50
  • @Greg: So before you get facetious, try `var_dump($_FILES)` on your php page and tell me how far off I am. – Brad Christie Jul 15 '11 at 16:52
  • Your extremly far off. var_dump($_FILES) leaves an empty array. – GAgnew Jul 15 '11 at 16:55
  • @Greg: In the future, before getting huffy with people trying to help you, you may consider being more explicit in your question and avoid any margin for alternative interpretation. Having said that, based on the (new) information presented in the question, saving the "BLOB" as you're calling it should be pretty straight forward given it's standand raw-image dumps for html elements. – Brad Christie Jul 15 '11 at 17:01
  • @BradChristie I have no reason to be facetious, I recognize that your trying to help, however, if after I tell you that 'its a raw string blob submitted via AJAX', don't come back and tell me I'm wrong. Its _my_ code. I _know whats happening with my code_. Is there a reason why a 'traditional post method' would kill my binary data? If its represented as a string (it is), the binary should not be changed, yes? – GAgnew Jul 15 '11 at 18:13
  • @Greg: My skepticism originated from you referring an ASCII encoded piece of data (base64 prefaced with an ASCII header) and referring to it as a [BLOB](http://en.wikipedia.org/wiki/Binary_large_object) (which, by definition, is binary). Sending an image as binary is far different then sending it as an encoded URI, which is why I was concerned with if you were referring the correct parts. Regardless, I still conclude it as a misinterpretation of information, which was later cleared up in your revision. My apologies if my tact was lacking, my intent is simply to provide an accurate answer. – Brad Christie Jul 15 '11 at 18:23
0

If it's really a blob, you might want to trying using mode "wb" as the second parameter to your fopen() call.

EDIT: You might also consider just using file_put_contents(), which is binary-safe.

timdev
  • 61,857
  • 6
  • 82
  • 92
0

This function will save data uri to file:

function saveDataUri($blob, $filename = null) 
{

    // generate unique name basing on content
    if (empty($filename)) {
        $filename = md5($blob);
    }

    // parse data URI
    $semiPos = strpos($blob, ';', 5);
    $comaPos = strpos($blob, ',', 5);
    $mime = substr($blob, 5, $semiPos - 5);
    $data = substr($blob, $comaPos + 1);

    $isEncoded = strpos(substr($blob, $semiPos, $comaPos), 'base64');

    if ($isEncoded) {
        $data = base64_decode($data);
    }


    // save image data to file
    switch ($mime) {
           case 'image/png':
                $ext = 'png';
            break;
           case 'image/gif':
                $ext = 'gif';
                break;
           case 'image/jpg':
           case 'image/jpeg':
           default:
                $ext = 'jpg';
                break;  
    }

    $outFile = $filename . '.' . $ext;
    $funcName = 'image' . $ext;
    $result = $funcName(imagecreatefromstring($data), $outFile);

    if ($result) {

        return $outFile;
    }

    return $result;
}

Usage in your case:

// some_validation($_POST);
$filename = saveDataUri($_POST['logoImage']);
echo '<img src="' . $filename . '" alt="' . $filename . '" />';
takeshin
  • 49,108
  • 32
  • 120
  • 164