PHP ZipArchiveクラスでのファイル名の文字化け

ZipArchive

ファイル名の文字化け(圧縮編)

PHPのZipArchiveクラスを使用した際にファイル名に日本語(全角文字)が含まれていると解凍したあとのファイル名が文字化けするという現象に遭遇しましたので検証してみました。
※環境は、Windows10 [Version 10.0.19043.1165]、PHPのバージョンは、8.0.3 です。
検証として、ファイル名が「新しいテキスト ドキュメント.txt」というファイルを ZipArchiveクラスを使用してまずは圧縮してみます。

ファイル名に日本語が含まれるファイルを C:\testフォルダに作成します。

圧縮は、ZipArchiveクラスの addFile関数を使用します。

$zip_file_path = 'C:/zipdir/before.zip';
$file_path     = 'C:/test/新しいテキスト ドキュメント.txt';
$entry_name    =  basename( $file_path );

$zip = new ZipArchive();
$result = $zip->open( $zip_file_path, ZipArchive::CREATE | ZipArchive::OVERWRITE );
if ( true === $result ) {
	$zip->addFile( $file_path, $entry_name );
	$zip->close();
}
before.zip ファイルができました。エクスプローラー上では、txtファイルは「新しいテキスト ドキュメント.txt」と表示されています。

圧縮ファイルが出力されましたので、手動操作で解凍しフォルダの中身を確認します。

手動で解凍してみました。
ファイル名が文字化けしています。

解凍したファイルのファイル名が文字化けしています。どうやら addFile関数の第2引数の $entry_name を UTF-8 から シフトJIS へ変換する必要があるようです。

$zip_file_path = 'C:/zipdir/after.zip';
$file_path     = 'C:/test/新しいテキスト ドキュメント.txt';
$entry_name    =  basename( $file_path );

$zip = new ZipArchive();
$result = $zip->open( $zip_file_path, ZipArchive::CREATE | ZipArchive::OVERWRITE );
if ( true === $result ) {
	$zip->addFile( $file_path, mb_convert_encoding( $entry_name, 'SJIS', 'UTF-8' ) );
	$zip->close();
}

コードを修正後、再度実行して圧縮し、手動で解凍してみたところファイル名の文字化けは解消されました。

after.zip ファイルです。手動で解凍しました。
文字化けは解消されています。

ファイル名の文字化け(解凍編)

圧縮ができましたので、次に、ZipArchiveクラスの解凍処理を行ってみます。解凍する zipファイルは、圧縮の検証で作成したものを使用します。

$zip_file_path = 'C:/zipdir/after.zip';
$unzip_dir     = 'C:/unzipdir';

$zip = new ZipArchive();
$result = $zip->open( $zip_file_path );
if ( true === $result ) {
	$index = 0;
	$file_name = $zip->getNameIndex( $index );
	$result = $zip->extractTo( $unzip_dir, $file_name );
	if ( ! $result ) {
		echo 'extractTo error:' . $result;
	}
	$zip->close();
}

解凍処理は、extractTo関数を使用しています。エラーは無かったようです。解凍結果はどうなったでしょうか?

またしてもファイル名が文字化けしています。

こちらもどうやらなのですが、getNameIndex関数が返すファイル名が別の文字コードに変換されてしまっているようです。ですので、 getNameIndex関数の第2引数にて、ZipArchive::FL_ENC_RAW(何も手を加えない文字列を取得します)を指定し、 getNameIndex関数が返すファイル名をシフトJISコードで返すようにします。その後、mb_convert_encoding関数を使用し、シフトJISからUTF-8へ変換します。var_dump関数を使用して、 mb_convert_encoding関数 の戻り値を表示してみます。

$zip_file_path = 'C:/zipdir/after.zip';
$unzip_dir     = 'C:/unzipdir';

$zip = new ZipArchive();
$result = $zip->open( $zip_file_path );
if ( true === $result ) {
	$index = 0;
	// getNameIndex( $index )でファイル名を取得すると、不明な文字コードで変換されたファイル名を取得してしまうため、
	// getNameIndex()の第2引数で ZipArchive::FL_ENC_RAW(何も手を加えない文字列を取得します)を指定します。
	$file_name = $zip->getNameIndex( $index, ZipArchive::FL_ENC_RAW );
	// SJISのファイル名をUTF-8へ変換します。
	$file_name = mb_convert_encoding( $file_name, 'UTF-8', 'SJIS' );

	var_dump( $file_name );

	$result = $zip->extractTo( $unzip_dir, $file_name );
	if ( ! $result ) {
		echo "extractTo error:" . $result;
	}
	$zip->close();
}
string(44) "新しいテキスト ドキュメント.txt"
extractTo error:

解凍後のファイル名は、”新しいテキスト ドキュメント.txt” として正しく取得できました。ただ、extractTo関数にてエラーとなり、解凍ファイルが作成されていません。さて、何がいけないのでしょうか。。。試しに、ZipArchiveクラスのインスタンス内で保持している”エントリ”の情報を statIndex関数を使用して参照してみます。

 	// ZipArchiveオブジェクト内で保持しているエントリを参照します。
	$info = $zip->statIndex( $index );
	var_dump( $info );
array(8) {
  ["name"]=>
  string(50) "ÉVé╡éóâeâLâXâg âhâLâàâüâôâg.txt"
  ["index"]=>
  int(0)
  ["crc"]=>
  int(93798387)
  ["size"]=>
  int(60)
  ["mtime"]=>
  int(1629592138)
  ["comp_size"]=>
  int(60)
  ["comp_method"]=>
  int(0)
  ["encryption_method"]=>
  int(0)
}

“name” 値が文字化けしています。これが原因のようです。”エントリ”内の情報も適正な値にする必要がありそうです。”エントリ” の ”name” 値は、renameIndex関数で変更できそうです。

$zip_file_path = 'C:/zipdir/after.zip';
$unzip_dir     = 'C:/unzipdir';

$zip = new ZipArchive();
$result = $zip->open( $zip_file_path );
if ( true === $result ) {
	$index = 0;
	// getNameIndex( $index )でファイル名を取得すると、不明な文字コードで変換されたファイル名を取得してしまうため、
	// getNameIndex()の第2引数で ZipArchive::FL_ENC_RAW(何も手を加えない文字列を取得します)を指定します。
	$file_name = $zip->getNameIndex( $index, ZipArchive::FL_ENC_RAW );
	// SJISのファイル名をUTF-8へ変換します。
	$file_name = mb_convert_encoding( $file_name, 'UTF-8', 'SJIS' );

 	// ZipArchiveオブジェクト内で保持しているエントリを参照します。
	$info = $zip->statIndex( $index );
	var_dump( $info );

	// ZipArchiveオブジェクト内で保持しているエントリの name もUTF-8へ変換した名前で置き換えます。
	$zip->renameIndex( $index, $file_name );

	$result = $zip->extractTo( $unzip_dir, $file_name );
	if ( ! $result ) {
		echo "extractTo error:" . $result;
	}
	$zip->close();
}

ファイル名が文字化けせずに無事解凍ファイルが作成されました。

タイトルとURLをコピーしました