On my page, people can choose to either view a pdf-file (on screen) or to download it. (to view it later on when they're offline)
When users choose to download, the code is executed once. I am keeping track of this with a counter and it increments by 1 for each download. So, this option is working fine and can be seen in the if-block below.
When users choose to view the file, the pdf file is displayed - so that's OK - but the counter increments by 2 for each view. This code is run from the else-block below.
I also checked the "Yii trace" and it is really going through all of it twice, but only when viewing the file...
if ($mode==Library::DOWNLOAD_FILE){
//DOWNLOAD
Yii::app()->getRequest()->sendFile($fileName, @file_get_contents( $rgFiles[0] ) );
Yii::app()->end();
}
else {
//VIEW
// Set up PDF headers
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="' . $rgFiles[0] . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($rgFiles[0]));
header('Accept-Ranges: bytes');
// Render the file
readfile($rgFiles[0]);
Yii::app()->end();
}
}
I tried a few other options, just to see how it would cause this to run twice:
So, it's only when going through the else-block that all of it (Yii request) is executed twice...
Thanks in advance for any suggestions...
I think that is because with the sendFile()
method you open the file actually just once, and in the else branch you really open it twice.
In the if branch you open the file once with the file_get_contents()
and pass the file as a string to the sendFile()
method and then it counts the size of this string, outputs headers, etc: http://www.yiiframework.com/doc/api/1.1/CHttpRequest#sendFile-detail
In the else branch you open the file first with the filesize()
and then also with the readfile()
method.
I think you could solve this problem by rewriting the else branch similar to the sendFile() method:
Basically read in the file with file_get_contents()
into a string, and then count the length of this string with mb_strlen()
. After you output the headers, just echo the content of the file without reopening it. You could even copy-paste the whole sendFile()
method into the else branch, just change the "attachment" to "inline" in the line (or replace this whole if/else statement with the sendFile method and just change the attachment/inline option to download or view, an even more elegant way would be overriding this method and extending with another parameter, to view or download the given file) :
header("Content-Disposition: attachment; filename=\"$fileName\"");
So I think something like this would be a solution:
// open the file just once
$contents = file_get_contents(rgFiles[0]);
if ($mode==Library::DOWNLOAD_FILE){
//DOWNLOAD
// pass the contents of file to the sendFile method
Yii::app()->getRequest()->sendFile($fileName, $contents);
} else {
//VIEW
// calculate length of file.
// Note: the sendFile() method uses some more magic to calculate length if the $_SERVER['HTTP_RANGE'] exists, you should check it out if this does not work.
$fileSize=(function_exists('mb_strlen') ? mb_strlen($content,'8bit') : strlen($content));
// Set up PDF headers
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="' . $rgFiles[0] . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $fileSize);
header('Accept-Ranges: bytes');
// output the file
echo $contents;
}
Yii::app()->end();
I hope this solves your problem, and my explanations are understandable.