0

Since the MS Excel is not able to recognize CSV files as of utf-8 encoding i tried to manually create such a file prepending the BOM prefix. But in my case i need to send it for user/browser to open rather than to save it on disk. The trick is that with small amount of data ~1-10 it works well, yet when of bigger number of records to be out, it fails to output in browser (Google Chrome, FF), rather it prints result as an HTML. You might try to play with it varying count parameter in GET request: http://tarex.ru/index.php?r=user/pricelist&file=1&count=100

The code

        $limit =  $_GET['count'] ? $_GET['count'] : 10;
        $filename= 'test.csv';  
        $out = fopen('php://output', 'w');  // open to out in browser
        fwrite($out, "\xEF\xBB\xBF");  // prepend BOM  
        $counter=0;
        foreach(Assortment::model()->findAll(  'measure_unit<>"" AND price>0' ) as $d)
        {
            $arr = array( $d->article2, $d->title, $d->oem,  $d->make, $d->availability , $d->getPrice(Yii::app()->user->id), $d->getDiscountOpt(Yii::app()->user->id) );
            fputcsv($out, $arr, ';'); //delimiter - ;
            if ($counter++ > $limit) break;
        }  
        header('Content-type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        fclose($out);   

Is there any solution?

Update

Eventually i've done it with Yii::app()->request->sendFile after turutosiya's suggestion.

    $filename='test.csv';         
    $filepath = Yii::app()->basePath . '/../files/'. $filename;
    $out      = fopen($filepath, 'w'); 
// writing csv ...
    fwrite($out, "\xEF\xBB\xBF");  //  put BOM at the beginning of file
    fputcsv($out, $arr, ';');
    foreach(Assortment::model()->findAll() as $d)
    {
        $arr = array( $d->article2, $d->title, $d->oem,  $d->make, $d->availability);
        fputcsv($out, $arr, ';'); // separator - ;  
    }  
    fclose($out); 
    Yii::app()->request->sendFile($filename,   @file_get_contents($filepath)); 
Community
  • 1
  • 1
Igor Savinkin
  • 5,669
  • 8
  • 37
  • 69
  • the header() method is not used for setting header-information on the $out resourcehandler... I think when it is working is only when Chrome, FF are guestimating the filetype – RichardBernards Nov 14 '14 at 15:24
  • when i remove it `header() method`, the output is always on screen (html) – Igor Savinkin Nov 14 '14 at 15:27
  • the header() business applies **ONLY** to the http portion. it doesn't change anything about the data you're sending, or how it'll be saved/viewed on the client. By removing the content-type header, you're just allowing the server to use its default mime type, which'll be text/html. – Marc B Nov 14 '14 at 15:29

2 Answers2

0

I suggest to use xSendFile() method for large file downloading.

$limit    =  $_GET['count'] ? $_GET['count'] : 10;
$filename = 'test.csv';
$filepath = '/path/to/xsendfile-enabled/dir/' . $filename;
$out      = fopen($filepath, 'w');
//
// write csv ...
//
fclose($out);
Yii::app()->request->xSendFile(filepath, array(
    'saveName' => $filename
));
turutosiya
  • 1,051
  • 10
  • 17
  • thank you for the method, you've given. But it returns an empty file: In the docs: *for this method to work the X-SENDFILE option/module should be enabled by the web server and a proper xHeader should be sent... If this option is disabled by the web server, when this method is called a download configuration dialog will open but the downloaded file will have 0 bytes.* This must be the case here. How to make this option up if the server is a shared server? – Igor Savinkin Nov 17 '14 at 15:01
  • @IgorSavinkin Would you ask a server admin to enable x-sendfile? – turutosiya Nov 18 '14 at 00:28
  • i've done it with just `Yii::app()->request->sendfile`. – Igor Savinkin Nov 18 '14 at 09:54
0

With the original code the problem probably was that you have the header statements at the end. If then the output is that large, that web server flushes the first block of data to the browser, then that browser receives the data without those headers. From that moment it is too late to send headers.

So generate those headers first:

    $limit =  $_GET['count'] ? $_GET['count'] : 10;
    $filename= 'test.csv';  
    $out = fopen('php://output', 'w');  // open to out in browser
    header('Content-type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    fwrite($out, "\xEF\xBB\xBF");  // prepend BOM  
    //
    // rest of output generating code comes here
    //
    fclose($out);   
trincot
  • 317,000
  • 35
  • 244
  • 286