Fails to serve downloaded file

Login or register to post comments
8 replies [Last post]
LaCeja's picture
Offline
Joined: 11/04/2009

Hi guys.

I'm having a very serious problem, that seems like it should be not so difficult. I'm trying to serve up a pdf file, which the user has requested. I took the lead from Giani's "QDataGridExporterButton.class.php", but I cannot get my new control to work.

Basically, I created a new class that extends QButton, just like Giani. I've called it "QJasperButton". Inside that control I have the working code to create a pdf file, using an interface with JasperReports. That interface works fine. It creates the report and puts it into a file. I have tested the file, by double clicking on it in the the file system and it opens correctly in Adobe Reader.

The problem I'm having is in serving that file as a download to the user. Here's the code that the new "QJasperButton" calls as a QAjaxAction:

protected function btnPrint_Click() {
// Create JasperReport and download to user
$txtReportName = "ShopCardDefaultwphoto";
$ParamArray = array();
$ParamArray = array('ParamOrderNo_int' => $this->txtOrderNo->Text,
'SUBREPORT_DIR' => __USER_REPORTS__);

$this->btnPrint->report_execute($txtReportName, $ParamArray, 1);
}

This is the method called to create the report and send it to the user:

public function report_execute($reportname, $ParamArray, $intKey ) {

  // make sure the JavaBridge extension is loaded
  $this->checkjavaExtension();
  // set the filename
  $filename = $reportname . ".pdf";

  // get the connection information for th edatabase
  foreach (unserialize(constant('DB_CONNECTION_' . $intKey)) as $key => $value) {
    if ($key == "server")
      $dbhost = $value;
    if ($key == "database")
      $dbname = $value;
    if ($key == "username")
      $dbuser = $value;
    if ($key == "password")
      $dbpass = $value;
  }
  $conn = null;

  // set the compiled iReport file name
  $report = __DOCROOT__ . __USER_REPORTS__ . $reportname . '.jasper' ;

  try {
    // Serialize parameters for Java
    $params = $this->report_parse_post_parameters($ParamArray);
    java( 'java.lang.Class' )->forName( 'com.mysql.jdbc.Driver' );
    // Connect to the database
    $conn = java( 'java.sql.DriverManager' )->getConnection(
    "jdbc:mysql://$dbhost/$dbname?user=$dbuser&password=$dbpass" );
    // Use the fill manager to produce the report.
    $fm = java('net.sf.jasperreports.engine.JasperFillManager');
    $pm = $fm->fillReport($report, $params, $conn);
    $exportManager = new JavaClass("net.sf.jasperreports.engine.JasperExportManager");
    $outputPath = realpath($filename);
    // Export the report to the file
    $exportManager->exportReportToPdfFile($pm, $outputPath);
    $conn->close();

    //Set Headers for downloading the report to the user
    header("Pragma: no-cache");
    header("Expires: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
    header('Cache-Control: private');
    header('Content-Description: File Transfer');
    header("Content-Disposition: attachment; filename=$filename");
    header('Content-Type: application/pdf');
    header('Content-Transfer-Encoding: binary');
    header('Content-Length: ' . filesize($outputPath) );
    readfile($outputPath);

    return;
  }
  catch( Exception $ex ) {
    if( $conn != null ) {
      $conn->close();
    }

    throw $ex;
  }
}

I have also created a plain php script, outside of QCubed, and run it from the address line of the browser and it works correctly. It seems that something in QCubed is blocking the output. I suspect QCubed is outputting something ahead of the headers for the download file, but I have no idea what it might be.

I've been banging my head against a wall with this for more than a week. I was thinking it might have been the wait icon display that was causing the problem, but I removed it this morning and the problem persists.

I'm using QCubed 2.0.

I really need some help with this.

Thanks.

vakopian's picture
Offline
Joined: 04/08/2008

Hi LaCeja,

Try this:

<?php
// ... your logic for getting the file
ob_end_clean();
ob_start();
header('Content-Type: application/pdf');
header("Content-Disposition: inline; filename=$filename");
header('Content-Length: ' . filesize($outputPath) );
// ... whatever other headers you need
readfile($outputPath);
QApplication::$JavaScriptArray = array();
QApplication::$JavaScriptArrayHighPriority = array();
exit();
?>

Not that the last 3 lines are important: they make sure that nothing else is sent after the file. And the beginning ob_ calls are to clear the buffer before starting the output. I also have ob_start() and ob_flush() around my MyAppForm::Run() call at the bottom of index.php, but I don't remember if it was really necessary to get the pdf downloading working.

LaCeja's picture
Offline
Joined: 11/04/2009

Vartan:

No, I get the exact same results, which is to say, no results. I get no output whatsoever. I also added the ob_start() and ob_flush() around my MYForm::Run(), like so:

ob_start();
OrderhdrsEditForm::Run('OrderhdrsEditForm');
ob_flush();

Offline
Joined: 02/02/2010

a very simple javascript solution could be:

QApplication::ExecuteJavaScript("window.location.href = '" . $strFile . "'");

this works for me

vakopian's picture
Offline
Joined: 04/08/2008

LaCeja,

When you say you get no output, how have you verified that? The best way to verify it would be to use firebug and look at the response and see if you have anything there at all.
If nothing is there, then you have to start debugging and see at what point the output gets lost.

I know that the code snippet I gave works in my application.

LaCeja's picture
Offline
Joined: 11/04/2009

Vartan:

This is the response header from Firebug:

<?xml version="1.0" encoding="UTF-8"?><response><controls><control id="Qform__FormState">438fe4f4a7a850824f5b80c2f46974bd</control></controls><commands></commands></response>

I have also done a trace, using xdebug and there are no errors at all in the trace.

I have also tried Mike's suggestion and I get the following error message:

Firefox doesn't know how to open this address, because the protocol (c) isn't associated with any program.

I have checked the Firefox options and the PDF is set to "Adobe Reader 9.3 (default)".

However, if I simply put the same path, being passed to Mike's javascript, into the address bar, the file opens as expected.

Offline
Joined: 02/02/2010

Ok now it is clear: your path is wrong!

try to use c:/test.com as url in firefox --> the message is protocoll c is not supported

maybe this will get you the correct url:
this gets you the relative path to your __DOCROOT__ !

$pathToPdf = '.' .substr($pathToPdf,strlen(__DOCROOT__));

vakopian's picture
Offline
Joined: 04/08/2008

If that's the response you're getting then something is still coming from other controls. You should not have anything from qcubed in the response. That's the whole point for ob_end_clean and ob_start. Maybe something has already been sent to the browser before ob_end_clean() was called (most likely with ajax).

With Mike's suggestion you actually have to compose the proper URL that points to the file, you can't just set the location.href to your file path on the server.

LaCeja's picture
Offline
Joined: 11/04/2009

Mike, your solution works perfectly! Thank you very much!!! Actually, it works better for me than doing a download, because it's less work for my users to print the report and return to the original page for further processing.

Vartan, thank you very much for your efforts. At this point, I have no idea what is causing the additional output. The form does have several QPanels (three of them with QSortable controls), but when the user presses the QJasperButton, the local method is called, which calls the "execute_report" method in the QJasperButton class. After creating the headers, it returns to the calling method in the main form and it returns. There are no other methods called that I'm aware of. One other thing, the QJasperButton has CausesValidation = false.

At this point it's no longer a problem. However, in other programs it may very well be a problem. However, this is the most complex program in the system. The rest of them have one or two additional QPanels or none at all. So, if the problem happens in the future, I can do an xdebug trace on a far less complicated program.

Again, thank you both for your help!