質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

2回答

4956閲覧

phpでファイルサイズが大きいbase64をdecodeしたい

3104_jp

総合スコア8

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

2クリップ

投稿2020/04/23 12:18

phpのbase64エンコードされたファイルをデコードしたいです。ファイルサイズが大きくなるケースも事前に想定しており、
freadで特定バイトずつ読み込み、デコードしています。
想定ではこれで上手くいくはずでしたが、どうしても上手くデコードができません。

なにか上手いやり方などありますでしょうか。

前提条件

  • エンコードされているのはzip圧縮されたデータ
  • 規定のAPIがbase64エンコードされた値を返す(仕様はこれで固定)
  • APIから取得したエンコード済みbase64ファイルをデコードし、zipファイルへ変換したい。

バージョン

  • php5.5

既知の問題

  • デコードが正しく処理されない。(zip解答時にCRCが一致しないと怒られる。)

該当のコード

PHP

1#$localFileにAPIから取得したbase64テキストを保存してます。 2 header('Content-Type: application/zip'); 3 header('Content-Disposition: attachment; filename="'. basename($localFile) .'.zip"'); 4 while (ob_get_level() > 0) { 5 ob_end_flush(); 6 } 7 ob_start(); 8 9 if($file = fopen($localFile , 'rb')){ 10 $bufferSize = 4096; 11 while(!feof($file)){ 12 $buffer = fread($file, $bufferSize); 13 echo base64_decode($buffer); 14 ob_flush(); 15 flush(); 16 } 17 fclose($file); 18 } 19 ob_end_clean();

他に試したこと

  • bufferSizeの部分の数値を変えると、ダウンロードされるzipファイルのサイズも微妙に異なってくる。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

興味があったので手元の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()が上手く判定する)

解決方法

  1. 一回の読み込みを(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を作成

text

1ああああ

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 14:49

編集2020/04/23 15:15
tanat

総合スコア18727

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tanat

2020/04/23 15:40 編集

質問のコードから想像するに 巨大なファイルを一回でbase64_decode()するにはメモリ制約がある環境である→nバイトずつ読み込んで出力してしまいたい という要件なのかなと。 base64のデコード自体は(base64として破綻していない限りは)PHPのbase64_decode()が完璧にやってくれるので、 base64として破綻しない切り出し方をどうやるかということを主眼に置いた回答です。
guest

0

ご提示いただいたものでうまく行けました!
この辺の処理が不足していたようでした。

ありがとうございました。
また、base64といってもいろいろな種類があるのも勉強になりました。

$bufferSize = 76+strlen(PHP_EOL);

投稿2020/04/27 02:32

3104_jp

総合スコア8

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tanat

2020/04/27 04:18

フィードバックありがとうございます。 うまく行ったようでよかったです
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問