Output a file for user to save

UPDATE: I’ve resolved my problem - lack of permissions on the folder was causing problems.

Hello all,

I am using a PHP Excel class to create .xls and .xlsx files. I want, at tne end of this processing, for the file to be output to the browser for the client to save/open. I can get to that stage but the file is empty. Here is a snippet of my code:


            $filename = "Zonal_Report_" . date('H.i_d.m.y') . ".xlsx";
        
            header("Content-type: application/vnd.ms-excel");
            header("Content-Disposition: attachment; filename=$filename");

            // now make file
            if ($format == 'xlsx')
            {
                $objWriter = new PHPExcel_Writer_Excel2007($sheet);
                $objWriter->save('php://output');
            }
            else if ($format == 'xls')
            {
                $objWriter = new PHPExcel_Writer_Excel5($sheet);
                $objWriter->save('php://output');
            }

Now the $objWriter->save() function, (not written by myself) is as follows:


    public function save($pFilename = null)
    {
        if (!is_null($this->_spreadSheet)) {
            // Create string lookup table
            $this->_stringTable = array();
            for ($i = 0; $i < $this->_spreadSheet->getSheetCount(); $i++) {
                $this->_stringTable = $this->getWriterPart('StringTable')->createStringTable($this->_spreadSheet->getSheet($i), $this->_stringTable);
            }

            // Create styles dictionaries
            $this->_stylesHashTable->addFromSource(             $this->getWriterPart('Style')->allStyles($this->_spreadSheet)             );
            $this->_stylesConditionalHashTable->addFromSource(     $this->getWriterPart('Style')->allConditionalStyles($this->_spreadSheet)             );
            $this->_fillHashTable->addFromSource(                 $this->getWriterPart('Style')->allFills($this->_spreadSheet)             );
            $this->_fontHashTable->addFromSource(                 $this->getWriterPart('Style')->allFonts($this->_spreadSheet)             );
            $this->_bordersHashTable->addFromSource(             $this->getWriterPart('Style')->allBorders($this->_spreadSheet)             );
            $this->_numFmtHashTable->addFromSource(             $this->getWriterPart('Style')->allNumberFormats($this->_spreadSheet)     );

            // Create drawing dictionary
            $this->_drawingHashTable->addFromSource(             $this->getWriterPart('Drawing')->allDrawings($this->_spreadSheet)         );
            
            // Create new ZIP file and open it for writing
            $objZip = new ZipArchive();
            
            // Try opening the ZIP file
            if ($objZip->open($pFilename, ZIPARCHIVE::OVERWRITE) !== true) {
                throw new Exception("Could not open " . $pFilename . " for writing!");
            }
            
            // Add [Content_Types].xml to ZIP file
            $objZip->addFromString('[Content_Types].xml',             $this->getWriterPart('ContentTypes')->writeContentTypes($this->_spreadSheet));
            
            // Add relationships to ZIP file
            $objZip->addFromString('_rels/.rels',                     $this->getWriterPart('Rels')->writeRelationships($this->_spreadSheet));
            $objZip->addFromString('xl/_rels/workbook.xml.rels',     $this->getWriterPart('Rels')->writeWorkbookRelationships($this->_spreadSheet));
            
            // Add document properties to ZIP file
            $objZip->addFromString('docProps/app.xml',                 $this->getWriterPart('DocProps')->writeDocPropsApp($this->_spreadSheet));
            $objZip->addFromString('docProps/core.xml',             $this->getWriterPart('DocProps')->writeDocPropsCore($this->_spreadSheet));
                    
            // Add theme to ZIP file
            $objZip->addFromString('xl/theme/theme1.xml',             $this->getWriterPart('Theme')->writeTheme($this->_spreadSheet));

            // Add string table to ZIP file
            $objZip->addFromString('xl/sharedStrings.xml',             $this->getWriterPart('StringTable')->writeStringTable($this->_stringTable));
            
            // Add styles to ZIP file
            $objZip->addFromString('xl/styles.xml',                 $this->getWriterPart('Style')->writeStyles($this->_spreadSheet));
            
            // Add workbook to ZIP file
            $objZip->addFromString('xl/workbook.xml',                 $this->getWriterPart('Workbook')->writeWorkbook($this->_spreadSheet));

            // Add worksheets
            for ($i = 0; $i < $this->_spreadSheet->getSheetCount(); $i++) {
                $objZip->addFromString('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->_spreadSheet->getSheet($i), $this->_stringTable));
            }

            // Add worksheet relationships (drawings, ...)
            for ($i = 0; $i < $this->_spreadSheet->getSheetCount(); $i++) {
                
                // Currently, only drawing collection should be checked for relations.
                // In the future, update this construction!
                if ($this->_spreadSheet->getSheet($i)->getDrawingCollection()->count() > 0) {
                    // Worksheet relationships
                    $objZip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels',     $this->getWriterPart('Rels')->writeWorksheetRelationships($this->_spreadSheet->getSheet($i), ($i + 1)));
                }
                
                
                // If sheet contains drawings, add the relationships
                if ($this->_spreadSheet->getSheet($i)->getDrawingCollection()->count() > 0) {
                    // Drawing relationships
                    $objZip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->_spreadSheet->getSheet($i)));
                    
                    // Drawings
                    $objZip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->_spreadSheet->getSheet($i)));
                }
                
            }
            
            // Add media
            for ($i = 0; $i < $this->getDrawingHashTable()->count(); $i++) {
                $objZip->addFromString('xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getFilename(), file_get_contents($this->getDrawingHashTable()->getByIndex($i)->getPath()));
                //The line underneath does not support adding a file from a ZIP archive, the line above does!
                //$objZip->addFile($this->getDrawingHashTable()->getByIndex($i)->getPath(), 'xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getFilename());
            }

            // Close file
            $objZip->close();
        } else {
            throw new Exception("PHPExcel object unassigned.");
        }
    }

I get the file to output but it’s empty/corrupted/not valid. I figure i’m doing something wrong here - perhaps the fact that the function is using some Zip thing? I have --enable-zip in my phpInfo so I don’t think there’s an issue there (using PHP 5.2.4).

What am I doing wrong?

I am guessing from the code that Excel files are actually a zip type with many files (mostly XML format) inside them. In which case you are doing all right it seems.

What you can do is also add as header the file size in bytes ? Sometimes when file size is not present the browser gets half of the file and so on…

Cheers!
Sumit

Is the php://output idea correct? How do I go about finding the file size of the file that’s created?

I don’t really understand how I’m supposed to get the file though. The file my code creates is 0 bytes (on download). It seems it’s not taking the php://output or something…

You can rather use a temporary file to save on server. And then using fopen send the file for download. You can easily get file size that way too using [URL=http://www.php.net/manual/en/function.filesize.php]filesize.

Cheers!
Sumit

I wouldn’t mind doing that because by default, the function above takes a filename and saves the file - however, where does PHP’s zipArchive function save the file? I don’t know how to access it again.