興味があったので手元のRaspbianで試してみたところ、スマートでは無いですが解決できたので回答します。
原因
- base64エンコードすると76バイト毎に改行コードが挿入される(Base64の仕様(必須では無いがMIMEの仕様に合わせるためにデフォルトではそうなる実装が多いっぽい?))
RFC The Base16, Base32, and Base64 Data Encodings
3.1. Line Feeds in Encoded Data
MIME [4] is often used as a reference for base 64 encoding. However,
MIME does not define "base 64" per se, but rather a "base 64 Content-
Transfer-Encoding" for use within MIME. As such, MIME enforces a
limit on line length of base 64-encoded data to 76 characters. MIME
inherits the encoding from Privacy Enhanced Mail (PEM) [3], stating
that it is "virtually identical"; however, PEM uses a line length of
64 characters. The MIME and PEM limits are both due to limits within
SMTP.
Implementations MUST NOT add line feeds to base-encoded data unless
the specification referring to this document explicitly directs base
encoders to add line feeds after a specific number of characters.
- その結果4のバイト倍数ずつ読み込むと改行コードも4バイトに含まれてしまい、Base64の1セットが崩れてしまう
- bufferSizeがファイルより大きい場合は改行コードも合わせてデコード出来るので問題ない(base64_decode()が上手く判定する)
解決方法
- 一回の読み込みを(76バイト+改行コード)の倍数のバイト数にし改行コードも含めてbase64_decode()に処理させる
ソース
PHP
1 //ファイル名はとりあえず決め打ち
2 $localFile = "./test.bat";
3
4
5 $readed = 0;
6 if($file = fopen($localFile , 'rb')){
7 //MIMEに合わせて一回で読むバイト数を決め打ち。合計値をn倍してもOK。
8 //ここではエンコード環境がlinuxなので改行コードがLF(1バイト)になっているが、エンコード環境によってはCRLF(2バイト)になるはずなので、扱いたいファイルの仕様に合わせて変更する
9 $bufferSize = 76+strlen(PHP_EOL);
10 while(!feof($file)){
11 $buffer = fread($file, $bufferSize);
12 echo base64_decode($buffer);
13 //改行コード分読み捨てる。回答の環境では圧縮から全部linuxなのでPHP_EOLでバイト数を判定しています。実際のバイナリに合わせて変更する必要があるかも
14 fread($file,strlen(PHP_EOL));
15 }
16 fclose($file);
17 }
18
検証
PHPのバージョン(検証していませんが、PHP5.5でも動くと思う)
bash
1 $ php -v
2PHP 7.3.11-1~deb10u1 (cli) (built: Oct 26 2019 14:14:18) ( NTS )
3Copyright (c) 1997-2018 The PHP Group
4Zend Engine v3.3.11, Copyright (c) 1998-2018 Zend Technologies
5 with Zend OPcache v7.3.11-1~deb10u1, Copyright (c) 1999-2018, by Zend Technologies
6
テキストファイルの作成
UTF-8でtest.txt
を作成
zip化
bash
1$ zip test.zip test.txt
2 adding: test.txt (deflated 38%)`
base64dにエンコード
bash
1$ base64 test.zip > test.bat`
エンコードされた内容の確認
改行コードが入っていることがわかる
bash
1$ cat test.bat
2UEsDBBQAAAAIAPu8l1C+TvKVCAAAAA0AAAAIABwAdGVzdC50eHRVVAkAA7qooV66qKFedXgLAAEE
36AMAAAToAwAAe9zY9BiGuABQSwECHgMUAAAACAD7vJdQvk7ylQgAAAANAAAACAAYAAAAAAABAAAA
4pIEAAAAAdGVzdC50eHRVVAUAA7qooV51eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBOAAAASgAA
5AAAA
bash
1$ cat -e test.bat
2UEsDBBQAAAAIAPu8l1C+TvKVCAAAAA0AAAAIABwAdGVzdC50eHRVVAkAA7qooV66qKFedXgLAAEE$
36AMAAAToAwAAe9zY9BiGuABQSwECHgMUAAAACAD7vJdQvk7ylQgAAAANAAAACAAYAAAAAAABAAAA$
4pIEAAAAAdGVzdC50eHRVVAUAA7qooV51eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBOAAAASgAA$
5AAAA$
PHPでデコード
$ test.php > test_split.zip
元のzipと比較
$ diff test.zip test_split.zip
出力が無いので差分が無いことが確認出来る
解凍
bash
1$ unzip test_split.zip
2Archive: test_split.zip
3replace test.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
4 inflating: test.txt
解凍出来た。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2020/04/23 15:28
2020/04/23 15:40 編集