7

CSV ファイルを PHP 出力バッファーに書き込み、書き込みが完了したらそのファイルをクライアントのコンピューターにダウンロードする必要があります。(サーバーに書き込んでダウンロードしたかったのですが、運用サーバーでは書き込みアクセス権がないことがわかりました)。

次のPHPスクリプトがあります。

$basic_info = fopen("php://output", 'w');
$basic_header = array(HEADER_ITEMS_IN_HERE);

@fputcsv($basic_info, $basic_header);

while($user_row = $get_users_stmt->fetch(PDO::FETCH_ASSOC)) {
    @fputcsv($basic_info, $user_row);
}

@fclose($basic_info);

header('Content-Description: File Transfer');
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=test.csv');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize("php://output"));
ob_clean();
flush();
readfile("php://output");

どうすればいいのかわからない。CSV ファイルがダウンロードされますが、何も表示されません。ob_clean() および flush() コマンドの順序と関係があると思いますが、これらを順序付ける最良の方法はわかりません。

どんな助けでも大歓迎です。

4

3 に答える 3

11

あなたは少しやりすぎです。CSV を出力することだけを目的としてスクリプトを作成します。画面に直接出力するだけです。ヘッダーやバッファー、php://output などについては、まだ心配する必要はありません。

データを画面に適切に出力していることを確認したら、次のヘッダーを最初に追加します。

<?php
header("Content-disposition: attachment; filename=test.csv");
header("Content-Type: text/csv");
?>

...ファイルが適切にダウンロードされることを確認します。次に、必要に応じて他のヘッダーを追加できます (上記に含めたヘッダーは、これを機能させるために余分な作業をせずに自分で使用したものです。他のヘッダーは基本的に効率とキャッシュ制御のためのものであり、そのうちのいくつかは既に適切に処理されている可能性があります)サーバーによって異なり、特定のアプリケーションにとって重要な場合とそうでない場合があります)。

必要に応じて、 と で出力バッファリングを使用ob_start()ob_get_clean()て、出力内容を文字列に取得し、それを使用してContent-Length.

于 2013-04-02T17:06:11.983 に答える
3

エドソンの回答のコメントで述べたように、コードの最後の行で「ヘッダーは既に送信されました」という警告が表示されると予想していました。

header('Content-Length: '.$streamSize);

出力はこのヘッダーが送信される前に書き込まれるためですが、彼の例は問題なく動作します。

いくつかの調査により、次の結論に至りました。

出力バッファ (ユーザーのバッファでも、デフォルトの PHP のバッファでも) を使用するときは、HTTP ヘッダーとコンテンツを好きなように送信できます。どのプロトコルでも、本文の前にヘッダーを送信する必要があることはわかっていますが (したがって、「ヘッダー」という用語)、出力バッファー層を使用すると、PHP がこれを処理します。出力ヘッダー (header()、setcookie()、session_start()) を使用する PHP 関数は、実際には、ヘッダー バッファーを埋めるだけの内部 sapi_header_op() 関数を使用します。次に、printf() などを使用して出力を書き込むと、出力バッファー (1 つを想定) に書き込まれます。出力バッファーが送信される場合、PHP は最初にヘッダーの送信を開始し、次に本文の送信を開始します。PHP がすべてを処理します。この動作が気に入らない場合は、出力バッファー レイヤーを無効にする以外に選択肢はありません。

ほとんどの構成での PHP バッファーのデフォルト サイズは 4096 バイト (4KB) です。これは、PHP バッファーが最大 4KB のデータを保持できることを意味します。この制限を超えるか、PHP コードの実行が終了すると、バッファリングされたコンテンツは、 PHP が使用されているバックエンド (CGI、mod_php、FastCGI) に自動的に送信されます。PHP-CLI では、出力バッファリングは常に Off です。

Edson のコードが機能するのは、出力バッファーがバッファー サイズを超えていないために自動的にフラッシュされなかったからです (そして、最後のヘッダーが送信される前にスクリプトが明らかに終了していません)。

出力バッファ内のデータがバッファ サイズを超えるとすぐに、警告が発生します。または彼の例では、データが

$get_users_stmt->fetch(PDO::FETCH_ASSOC)

が大きすぎます。

これを防ぐには、ob_start() および ob_end_flush(); を使用して出力バッファリングを自分で管理する必要があります。以下のように:

// Turn on output buffering
ob_start();

// Define handle to output stream
$basic_info   = fopen("php://output", 'w');

// Define and write header row to csv output 
$basic_header = array('Header1', 'Header2');  
fputcsv($basic_info, $basic_header);  

$count = 0; // Auxiliary variable to write csv header in a different way

// Get data for remaining rows and write this rows to csv output
while($user_row = $get_users_stmt->fetch(PDO::FETCH_ASSOC)) {  
    if ($count == 0) {
        // Write select column's names as CSV header
        fputcsv($basic_info, array_keys($user_row));  
    } else {
        //Write data row
        fputcsv($basic_info, $user_row);
    }
    $count++;
}  

// Get size of output after last output data sent  
$streamSize = ob_get_length();

//Close the filepointer
fclose($basic_info);  

// Send the raw HTTP headers
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=test.csv');  
header('Expires: 0');  
header('Cache-Control: no-cache');
header('Content-Length: '. ob_get_length());

// Flush (send) the output buffer and turn off output buffering
ob_end_flush();

ただし、他の制限にはまだ縛られています。

于 2016-01-12T13:41:51.757 に答える
0

私はあなたのコードを微調整しました。

実際の出力が送信される前に、通常の HTML タグ、ファイル内の空白行、または PHP から header() を呼び出す必要があることに注意してください。

  • あまり変更されていないヘッダーをいくつか削除しました。
  • 選択した列の名前を使用して csv ヘッダーを書き込む別のオプションをコメントしました。
  • コンテンツの長さが機能するようになりました。
  • $basic_info は既に出力バッファーにあり、ヘッダーを介してファイル内にリダイレクトされているため、echo する必要はありません。
  • オーバーヘッドが発生する可能性があるため削除@されました (PHP エラー制御演算子)。現在表示するリンクはありませんが、検索すると見つかる可能性があります。エラーを黙らせる前によく考えるべきです。ほとんどの場合、エラーを黙らせるのではなく修正する必要があります。

header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=test.csv');  
header('Expires: 0');  
header('Cache-Control: no-cache'); 

$basic_info   = fopen("php://output", 'w');  
$basic_header = array(HEADER_ITEMS_IN_HERE);  

fputcsv($basic_info, $basic_header);  
$count = 0; // auxiliary variable to write csv header in a different way

while($user_row = $get_users_stmt->fetch(PDO::FETCH_ASSOC)) {  
    // Write select column's names as CSV header
    if ($count == 0) {
        fputcsv($basic_info, array_keys($user_row));  
    }

    fputcsv($basic_info, $user_row);  
    $count++;
}  

// get size of output after last output data sent  
$streamSize = ob_get_length(); 
fclose($basic_info);  

header('Content-Length: '.$streamSize);
于 2015-12-23T18:44:43.593 に答える