Reputation: 7315
I've got an application running under Drupal in a LAMP environment. It delivers images out of a database. On my personal machine (OS X Lion) it is working correctly. On the dev server (Amazon EC2) the exact same code doesn't work. Both servers run PHP 5.3. I've confirmed that the data is getting corrupted between echo $fileData
and it being received by the browser. Further, I've confirmed that the data is NOT corrupted if it is base64-encoded (I used a script to request the data, decode it, and save it to a file).
On the Drupal side, the code is in a module that creates a menu callback; the callback function echos the file data directly to avoid storing the entire image in memory (I was using PDO::FETCH_BOUND with PDO::PARAM_LOB to create a stream but it's going into a string right now while I'm trying to find the problem; that wasn't it). The menu entry has a custom delivery callback that essentially does nothing. I tried flushing any output buffering on the idea that Drupal might be trying too hard for Unicode support or something, but that didn't help either.
I'm hoping that somebody has an idea of what might be causing my problem. If I haven't been clear enough, I'll post some code; right now it's full of commented-out features so it would take some cleaning up.
Update 1:
I put together some example code but it did not exhibit the bug; I may convert the code into a Drupal module to test that code stack. However, I have narrowed down the bug to echo $fileData
. The code looks something like this:
function example_menu() {
$pages['mpicture/%'] = array(
'title' => 'Picture handler',
'page callback' => 'example_picture',
'page arguments' => array(1),
'delivery callback' => 'example_deliver_png',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
}
function example_picture($fileID) {
// Point 1
example_output_file($fileID);
}
function example_deliver_png($content) {
}
function example_output_file($fileID) {
$statement = db_select('mfiles', 'f')
->fields('f', array('fileType', 'fileSize', 'fileData', 'fileData64', 'lastModified'))
->condition('fileID', $fileID, '=')
->execute();
if ($file = $statement->fetchAssoc()) {
header('Content-Type: ' . $file['fileType']);
// Point 2
header('Content-Length: ' . $file['fileSize']);
echo $file['fileData'];
}
}
This does not work. If I change Point 2
to this:
// Point 2
header('Content-Length: ' . $file['fileSize']*4/3);
echo $file['fileData64'];
It works correctly, as long as my client runs a base64_decode
on the output. However, if I do this:
// Point 1
ob_start()
example_output_file($fileID);
$output = base64_encode(ob_get_clean());
header('Content-Length: ' . strlen($output));
echo $output;
...it doesn't work with the same base64_decode
ing client. How could that possibly be?
And yes, I am storing the data twice; in every single test I run it doesn't matter whether I use $file['fileData']
or base64_decode($file['fileData64'])
so I don't think it's a database issue.
Update 2:
But curiously, this works:
// Point 1
ob_start()
example_output_file($fileID);
$output = base64_encode(trim(ob_get_clean()));
header('Content-Length: ' . strlen($output));
echo $output;
So, I guess now I try to find where the white space gets printed?
Upvotes: 1
Views: 647
Reputation: 7315
I'm going to strangle the intern. I swear it. Not really, it's an expression...but I'm pissed.
The entire problem was because a new file he added had white space at the end of it. A simple "?>\n". This printed white space before every page on the server, including the image handler. The white space made the image corrupt.
I guess the moral here is to never close PHP tags at the end of files, and make damn sure that everyone you work with does the same.
Upvotes: 1