2

私はこのエラーの背後にある理論を知っていますが、今また私を夢中にさせています。アプリケーションでTonicを使用しています。このライブラリを使用すると、すべてのトラフィックをdispatch.phpスクリプトにリダイレクトし、スクリプトは適切な を実行し、によって表示 (出力) されるResourceResource返します。Responsedispatch.php

の出力方法は次のResponseようになります。

/**
 * Output the response
 */
public function output()
{
    foreach ($this->headers as $name => $value) {
        header($name.': '.$value, true, $this->responseCode());
    }
    echo $this->body;
}

知る限り、これは、.php の出力に何も書き込めないことを示していますResource

Resource入力 csv から動的に csv を生成し、それをブラウザーに出力する (データの 1 列を別の形式に変換する) があります。

$csv = fopen('php://output', 'w');
// sets header
$response->__set('contentDisposition:', 'attachment; filename="' . $fileName . '"');
while (($line = fgetcsv($filePointer, 0, ",", '"')) !== FALSE) {
    // generate line
    fputcsv($csv, $line);
}
fclose($filePointer);
return $response;

これは 100% 正常に動作します。ヘッダーに問題はなく、正しいファイルが生成され、ダウンロード用に提供されます。ヘッダーが設定される前に出力に書き込んでいるので、これはすでに混乱していますか? fputcsv は実際に何をしますか?

同様のことを行う 2 番目のリソースがありますが、カスタム ファイル形式 (テキスト ファイル) を出力します。

$output = fopen('php://output', 'w');
// sets header
$response->__set('contentDisposition:', 'attachment; filename="' . $fileName . '"');
while (($line = fgetcsv($filePointer, 0, ",", '"')) !== FALSE) {
    // generate a record (multiple lines) not shown / snipped
    fwrite($output, $record);
}
fclose($filePointer);
return $response;

唯一の違いは、fputcsv と bang の代わりに fwrite を使用することです。

 headers already sent by... // line number = fwrite()

これは非常に紛らわしいです!私見は、実際には両方の場合に失敗するはずですか?最初のものはなぜ機能するのですか?どうすれば2番目のものを機能させることができますか? (ファイルを含む巨大な文字列を生成し、それを応答本文に入れることができます。ただし、ファイルはかなり大きくなる可能性があり(最大50 mb)、これを避けたいと考えています。)

4

2 に答える 2

0

ここに私の解決策があります。誰かがこれよりも優れた(単純な)ものを思いつくかもしれないので、しばらくの間、回答としてマークするつもりはありません。

まず、fwrite と fputcsv に関するコメント:

fputcsv には完全に異なるソースがあり、fwrite との共通点はあまりありません (fwrite は内部で呼び出されず、C ソース コードでは別の関数です)。CI はなぜ動作が異なるのかわかりませんが、動作は異なります。

解決:

生成されたファイルは入力によっては「大きく」なる可能性があるため、文字列連結によってファイル全体を生成し、それをメモリに保持することは優れた解決策ではありません。

少しグーグルで調べたところ、Apache の mod_xsendfile が見つかりました。これは、ユーザーに送信されるファイルへのパスを含むカスタム ヘッダーを php に設定することで機能します。その後、mod はそのカスタム ヘッダーを削除し、ファイルを応答として送信します。

mod_xsendfile の問題は、私も使用している mod_rewrite と互換性がないことです。404 エラーが発生します。これを解決するには、追加する必要があります

RewriteCond %{REQUEST_FILENAME} !-f

apache config の適切な場所に移動します (要求が実際に物理的に存在するファイルに対するものである場合は、書き直さないでください)。しかし、それだけでは不十分です。ヘッダー X-Sendfile を設定する必要があるのは、書き換えられていない php スクリプトで、実際に存在する php ファイルです。

したがって、最後にファイルを生成する \Tonic\Resource クラスで、上記のスクリプトにリダイレクトします。

$response->__set('location', $url . "?fileName=" . urlencode($fileName));
$response->code = \Tonic\Response::FOUND;
return $response;

上記のスニペットにリダイレクトするダウンロード スクリプトでは、次のようにします (検証は省略します)。

$filePath = trim($_GET['fileName']);
header ('X-Sendfile: ' . $filePath);    
header ('Content-Disposition: attachment; filename="' . $filePath . '"');

生成されたファイルのダウンロード ダイアログがブラウザに表示されます。

生成されたファイルを削除するには、cron ジョブを作成する必要もあります。

/usr/bin/find /path/to/generatedFiles/ -type f -mmin +60 -exec rm {} + 

これにより、ディレクトリ内の 60 分より古いすべてのファイルが削除されます/path/to/generatedFiles

ファイルに追加できるように、ubuntuサーバーを使用しています

/etc/cron.daily/standard

またはそのディレクトリに新しいファイルを生成するか、/etc/cron.hourlyそのコマンドを含む新しいファイルを生成します。

ノート:

入力 csv ファイルの sha1 ハッシュの後に生成されたファイルに名前を付けて、名前が一意になるようにします。誰かが同じ要求を短期間に何度も繰り返した場合は、既に生成されたファイルをもう一度返すことができます。

于 2013-04-10T11:10:51.650 に答える
0

$recordが設定されていないため、レベル のエラーが発生しますNOTICEerror_reporting必要な場合true、PHP はヘッダーを送信する前にこのエラーを出力に入れます。

false に設定error_reportingし、代わりにログを監視してください。

于 2013-04-09T07:49:42.820 に答える