web-dev-qa-db-ja.com

PHP WindowsでのZipArchiveの破損

PHPのZipArchiveクラスを使用して、写真を含むZipファイルを作成し、それをブラウザーに提供してダウンロードします。これが私のコードです:

/**
 * Grabs the order, packages the files, and serves them up for download.
 *
 * @param string $intEntryID 
 * @return void
 * @author Jesse Bunch
 */
public static function download_order_by_entry_id($intUniqueID) {

    $objCustomer = PhotoCustomer::get_customer_by_unique_id($intUniqueID);

    if ($objCustomer):

        if (!class_exists('ZipArchive')):
            trigger_error('ZipArchive Class does not exist', E_USER_ERROR);
        endif;

        $objZip = new ZipArchive();
        $strZipFilename = sprintf('%s/application/tmp/%s-%s.Zip', $_SERVER['DOCUMENT_ROOT'], $objCustomer->getEntryID(), time());

        if ($objZip->open($strZipFilename, ZIPARCHIVE::CREATE) !== TRUE):

            trigger_error('Unable to create Zip archive', E_USER_ERROR);

        endif;          

        foreach($objCustomer->arrPhotosRequested as $objPhoto):

            $filename = PhotoCart::replace_ee_file_dir_in_string($objPhoto->strHighRes);
            $objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename)));

        endforeach;

        $objZip->close();

        header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($strZipFilename)).' GMT',  TRUE, 200);
        header('Cache-Control: no-cache', TRUE);
        header('Pragma: Public', TRUE);
        header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT', TRUE);
        header('Content-Length: '.filesize($strZipFilename), TRUE);
        header('Content-disposition: attachment; filename=press_photos.Zip', TRUE);

        header('Content-Type: application/octet-stream', TRUE);

        ob_start();
        readfile($strZipFilename);
        ob_end_flush();
        exit;

    else:

        trigger_error('Invalid Customer', E_USER_ERROR);

    endif;

}

このコードは、IE以外のすべてのブラウザーで非常にうまく機能します。 IEでは、ファイルは正しくダウンロードされますが、Zipアーカイブは空です。ファイルを抽出しようとすると、WindowsはZipアーカイブが破損していると通知します。誰かが以前にこの問題を抱えたことはありますか?

編集更新:@profitphpからの提案の後、ヘッダーを次のように変更しました:

header("Cache-Control: public");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
//header("Content-Description: File Transfer");
//header("Content-type: application/Zip");
header("Content-Disposition: attachment; filename=\"pressphotos.Zip\"");
//header("Content-Transfer-Encoding: binary");
header("Content-length: " . filesize($strZipFilename));

また、Firefoxで開いた後のWindowsのエラーのスクリーンショットは次のとおりです。

alt text

このエラーは、IEとWindows上のFirefoxの両方で発生します。Macでは正常に機能します。また、Windowsでは、ファイルサイズは正しいように見えます。

alt text

編集#2この問題は解決されました。以下の私の答えを参照してください。

20
Jesse Bunch

さて、多くの争いの後、私は問題を理解しました。この問題は、次のコード行に起因します。

$objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename)));

何らかの理由で、/press_photos/ Zipアーカイブ内のローカル(内部)ファイル名のパスの一部が原因で、WindowsはZipファイルが破損していると見なしていました。以下のように行を変更した後、WindowsはZipファイルを正しく開きました。ふぅ。

$objZip->addFile($filename,sprintf('%s-%s', $objPhoto->getEntryID(), basename($filename)));
6
Jesse Bunch

私はこれと同じ問題を抱えていました、そして私の解決策はこのスレッドの正解に似ていました。ファイルをアーカイブに入れるとき、絶対ファイル(スラッシュで始まるファイル)を持つことはできません。そうしないと、何らかの理由でWindowsで開くことができません。

それで、彼(Jesse Bunch、この記事の執筆時点で選択された回答)が含まれているフォルダーを削除したためではなく、開始スラッシュを削除したために機能しました。

変更して問題を修正しました

$Zip->addFile($file, $file); // $file is something like /path/to/file.png

// we make file relative by removing beginning slash so it will open in Windows
$Zip->addFile($file, ltrim($file, '/'));

そしてそれはWindowsで開くことができました!

それはおそらくpclzip(Plahcinskiの答え)が機能するのと同じ理由です。私はそれが最初のスラッシュを自動的に取り除くに違いない。

PHP ZipArchive::addFileドキュメントページに 特定のコメント がなければ、これを理解することはできなかったでしょう。

23
CWSpear

私は最近、あなたが説明したのと同様の問題を抱えていました。 ZipArchiveはせいぜい不安定であることがわかりました。

このシンプルなライブラリで問題を解決しました

http://www.phpconcept.net/pclzip

include_once('libs/pclzip.lib.php');

.。

function Zip($source, $destination){
$zipfile = new PclZip($destination);
$v_list = $zipfile->create($source, '', $source); }

$ source =圧縮したいフォルダ$ destination = Zipファイルの場所

私は2日間ZipArchiveを探し、PCLZipのすべての問題を5分で解決しました。

これがあなたとこの問題を抱えている他の人に役立つことを願っています(これは問題のトップグーグル結果に近いので)。

14
Plahcinski

この提案はすべてあなたを助けるかもしれませんが、私の場合はob_clean();を書く必要があります。最初のヘッダーの前( '');私が前に含めたいくつかのファイルは、WindowsでZipファイルを壊したいくつかの文字を印刷するためです。

$Zip=new ZipArchive();
$Zip->open($filename, ZIPARCHIVE::CREATE);
$Zip->addFile($file_to_attach,$real_file_name_to_attach);
$Zip->close();

ob_clean();
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header('Content-Type: application/x-download');
header('Content-Disposition: attachment; filename="file.Zip"');
readfile($filename);
exit;
9
Luca Camillo

ZipArchiveにはIBM850でエンコードされたエントリ名が必要なため、アンダースコアなどの特殊文字を使用すると問題が発生します。オンラインのコメントを参照してくださいPHPマニュアル: http://www.php.net/manual/en/function.ziparchive-addfile.php#95725

5
David Spector

私は以前にこれに問題がありました。コンテンツタイプヘッダーを削除してみてください。これが私が思いついたコードで、IEとFFで動作しました。コメントされた行に注意してください。同じ問題があり、それらの組み合わせが異なっていました。

header("Cache-Control: public");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
//header("Content-Description: File Transfer");
//header("Content-type: application/Zip");
header("Content-Disposition: attachment; filename=\"adwords-csv.Zip\"");
//header("Content-Transfer-Encoding: binary");
header("Content-length: " . filesize($filename)); 
3
profitphp

他の人が提案したことに加えて、ファイルとディレクトリの名前に注意を払うことが重要です Windowsは必ずしもLinuxのファイルパスと名前を好まないからです。また、圧縮時に異なる方法でエスケープすることもあります。例はたくさんありますが、最も重要なのは

  • *ドットファイル(。および..)、大文字と小文字の違いのみのファイル(name.txtおよびNAME.txt)、
  • 絶対ファイルパス(/tmp/file.txt)*。
  • Windowsのファイル名で許可されている他のいくつかの文字は、Windowsエクスプローラーを使用してファイルを開くときに問題を引き起こす可能性があります。私の場合、「:」のキャラクターは取引のブレーカーでしたが、これを見つけるために多くの作業を要しました。

したがって、exec( 'Zip ...')を介して多くのパラメーターの使用を再開する前に、簡単な手順に従うことをお勧めします。

  1. あなたのウェブサイトが圧縮するフォルダまたはファイルを見つけます。
  2. 実行:Zip -9 -r -k Zip-modified-names.Zip/path/to/your/folder
  3. コンソールが吐き出すものに注意してください。私の場合、ファイル名の「:」が削除されました。
  4. ZipファイルをWindowsマシンに移動し、開こうとします。

これが機能する場合は、ファイル/ディレクトリ名から-kオプションで削除された文字を削除して、通常どおりに圧縮することをお勧めします。 -kなどの一部のパラメーターには副作用があることに注意してください。この場合、-kは-qオプション(シンボリックリンクの場合)と矛盾します。

また、-kオプションを使用すると、ファイル名が読み取れなくなる場合があります。私の場合、ファイルはアーカイブから必要なレコードを簡単に見つけることができるように作成時間(10:55:39.pdfなど)に基づいて名前が付けられましたが、-kオプションを使用すると105539.pdfになり、ユーザーが簡単に読み取ることができなくなりました。そのため、名前を10_55_39.pdfに変更しました。これは、-kオプションを使用せずにWindowsで開きますが、引き続き読み取り可能です。

これに加えて、PCLZipを使用すると、フォルダー全体を一度に追加したり、ファイルのパスを1行で変更したりできるため、作業が大幅に楽になります。私の場合、2番目と3番目のパラメーターを使用してZipファイルから/ tmp/directory /を削除します。これにより、別のWindows互換性の問題(Zipファイルに絶対パスがある)が回避されます。

$v_list = $Zip->create( $sourceFolder, PCLZIP_OPT_REMOVE_PATH, $sourceFolder . DIRECTORY_SEPARATOR);
if ($v_list == 0) {
    throw new \Exception($Zip->errorInfo(true));
}
2
Shakus
            ob_clean(); //very important
            // http headers for Zip downloads
            header("Pragma: public");
            header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
            header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
            header("Cache-Control: public");
            header('Content-Type: application/x-download');
            header("Content-Disposition: attachment; filename=\"filename.Zip\"");
            header("Content-Length: ".filesize($filepath ));

            @readfile($filepath );
            unlink($filepath);  //very important
            exit;//very important

上記の解決策を試している間、これは私のために働きました。

1
Joshy Francis

私も同じ問題を抱えていました。このコードは私にとってはうまくいきましたが、phpファイルの最初の行に入力する必要がありました!ファイルの途中にコードを配置すると、機能しませんでした。多分いくつかのエンコーディングの問題?!

// Prepare File
$file = tempnam("tmp", "Zip");
$Zip = new ZipArchive();
$Zip->open($file, ZipArchive::OVERWRITE);

// Staff with content
$Zip->addFile($filepathOnServer, 'mypic.jpg');

// Close and send to users
$Zip->close();
header('Content-Type: application/Zip');
header('Content-Disposition: attachment; filename="filename.Zip"');
readfile($file);
unlink($file);
1
pu-c

これをすべて試しても頭を叩いてもうまくいかなかった方のために、このように問題を修正することができました。

$zipname = "download.Zip";
$Zip = new ZipArchive;
$Zip->open($zipname, ZipArchive::CREATE);
foreach ($files as $file) {
    # download file
    $download_file = file_get_contents($file); <- important

    #add it to the Zip
    $Zip->addFromString(basename($file), $download_file); <- important  
}
$Zip->close();

header('Content-Type: application/Zip');
//header('Content-disposition: attachment; filename='.$zipname);
header("Content-Disposition: attachment; filename=\"$zipname\"");
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
unlink($zipname);
exit;

私の問題は、ファイルパスが文字列として入ってくることでした。 (string)$pathを試しましたが、それでも機能しませんでした。私にとっては、ファイルのfile_get_contentsを使用し、次に組み込み関数addFromStringを使用してそれを実行しました。

0
Cesar Bielich

私はこの問題を1時間抱えています。 10種類の解決策を試した後、Zipファイルを出力した後にスクリプトが存在することを確認して解決しました。

            readfile($Zip_name);
            unlink($Zip_name);
            **exit();**
0
Mark

他の誰かが数時間レンガの壁にぶつけて、私のように苦しんでいる場合に備えて。私は同じ問題を抱えていましたが、PHPにいくつかのライブラリをロードし、そのうちの1つに?>コードの後に​​空の行があることに気付くまで、どのソリューションも役に立ちませんでした。 include(/path/to/lib/lib.php);が空の行を持つライブラリがブラウザに出力されたため、ZipはWindowsによって破損していると分類されました(Winzip、Total Commanderなどには問題はありませんでした)。したがって、インポートされたものがないことを確認してください。ライブラリまたは存在する場合は、スペースや空の行はありません。

0
Emil Borconi

Zipファイル名にタイムスタンプを使用していました。実際、Windowsファイルシステムは":\/*?<> |"のような特殊文字をサポートしていません。

時間部分から":"を削除すると、チャームのように機能します

0
Sameer Ahmed