PHP(サーバー)と C#(クライアント)で圧縮した文字列のやりとりをする

サーバーとクライアント間のデータやりとりは json が扱いやすいですが、場合によっては転送量が気になるケースもあるでしょう。

サーバー側で文字列を圧縮、クライアントで受け取った文字列を解凍(逆もあり)することで、転送量を減らし、場合によってはロードパフォーマンスを上げる方法です。様々なやり方があると思いますが、とりあえず動作するコードを挙げます。
.Net4.6、PHP Version 7.4.25、文字コード UTF-8 で確認。。

PHP側

圧縮

gzencode で圧縮かけたバイナリデータを、Base64でテキスト化します。
こうすることで POST での転送が容易になります。

// $json が元のテキストデータ
$encode_str = base64_encode(gzencode($json, 9));
echo $encode_str;

解凍

圧縮の逆の操作を行うことで、解凍を行えます。

// $json が元のテキストデータ
$json = gzdecode(base64_decode($encode_str));

C# 側

解凍

POST で受け取った encode_str を引数として渡し、解凍します。
C# のコードは PHP にくらべ、まどろっこしい。

using System.IO.Compression;

// $encode_str  == base64gzip
public static string Decompress(string base64gzip)
{
    byte[] compressed = Convert.FromBase64String(base64gzip);

    var buffer = new byte[1024];

    using (var ms = new MemoryStream())
    {
        using (var gzipStream = new GZipStream(new MemoryStream(compressed), CompressionMode.Decompress))
        {
            while(true)
            {
                var readSize = gzipStream.Read(buffer, 0, buffer.Length);
                if (readSize == 0)
                {
                    break;
                }
                ms.Write(buffer, 0, readSize);
            }
        }

        byte[] base64raw = ms.ToArray();
        return  Encoding.UTF8.GetString(base64raw, 0, base64raw.Length);
    }
}

圧縮

public static string Compress(string rawtext)
{
    byte[] uncompressed = Encoding.UTF8.GetBytes(rawtext);
    string s = Encoding.UTF8.GetString(uncompressed, 0, uncompressed.Length);
    using (var ms = new MemoryStream())
    {
        using (var gzipStream = new GZipStream(ms, CompressionLevel.Fastest))
        {
            gzipStream.Write(uncompressed, 0, uncompressed.Length);
        }

        return Convert.ToBase64String(ms.ToArray());
    }
}

実際に試してみる

動作確認のために、吾輩(猫)さんをお借りします。読み仮名も入っちゃってますが目を瞑るとして…。UTF-8、1468バイト。

吾輩わがはいは猫である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪どうあくな種族であったそうだ。この書生というのは時々我々を捕つかまえて煮にて食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌てのひらに載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始みはじめであろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶やかんだ。その後ご猫にもだいぶ逢あったがこんな片輪かたわには一度も出会でくわした事がない。のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙けむりを吹く。どうも咽むせぽくて実に弱った。これが人間の飲む煙草たばこというものである事はようやくこの頃知った。

PHP で圧縮する

ほとんど圧縮かかってないじゃん! と思うかもしれませんが UTF-8 マジックにより見た目よりは圧縮がかかっています。738 バイト、1/2 くらい。

もし json のような、フィールド名の繰り返しが多いデータであれば 1/8 くらいになる事もあります。

H4sIAAAAAAACA21U61raQBB99ewmKEgoCCJCaxObyiVg0LYq0kAfZrO74S06czZBPts/822ycztnzqzu7ct8oWRX
iVCJtRIBWRsulZgpIZVsK0/qXke3OrjdKxHb4I7dPKnEQokBedqrSEkKj5RoKzFXkn7Oy2lb7+hw6T6VOEOJ7/BZ
UXixG3IVKcvrwExGKH2jpKgt/YlMy2Mfv638RPn58WB+JvCfwkbFlnLGXEusy/mNOXtBOG4rCIDJEAauZz1ro8q0
2G4P167DgJuUhCtTsk/9MyKGOeKe+b+E26wUX5Xo4nwL4GvzecMk1ElczmLzQM7FxrPD1HYejUzB2BnyUHhq55kZ
dSuqRQICb+EQoy51lX3IzL1RubFUXsM0L8lSq6YzrImlATUJlG2QGw1xekgiRFXh5eLpdLIVNLZUN6N5cWaxxmjq
EM8H9hSeSTVl2dLNe5M9G9FDeIC5S+PdQksnzlyFQmK9yxnOpxBzofZ+UBJqsszJfuazxJV840GLuQlpOnfF5kKJ
fn0bUXvoLeb8/lD568o6tQQRi4dlduTzEd1enKJGDxkyz/QjaWZU9nZUCxyyqGrOs8Pd1bsSuOcQiuoqSTpp/185
ImPlk7rEH+jNidlV77DbcbJjqadj5qoKjBk1EYjOj1iK3xduTUzmKH1XtV2tSF1mTez1kYRvy6RxSEgGQzD2poTb
3EkFx+0ghVfWrcke51l5vbL5i5IBSBvUOoQ29hR4hZdhiVoxUL8cvG8nbIcMjQNT2zovcyeDCJJYsq42nt7SiKU+
3xb5BLR0cTuqtziEeNzzkoHDFDqZoPnMfomwVm7Ee0x2aVNRPr9+WPl6ldxPhmAXvzD3Bydgt0RKvMLN2bltjPFk
eZxZ9nXvjTus3joese7v+JYUK3bY4qnOCPhS508ngh+A/LBWSHZISO0eJS87rVqW/yqnkih4WCvZxFWAKkgS+za6
P1b5CwT7QUe7BQAA

C# で解凍する

上記の文字列を解凍すると、元の文章を復元できました。

吾輩わがはいは猫である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪どうあくな種族であったそうだ。この書生というのは時々我々を捕つかまえて煮にて食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌てのひらに載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始みはじめであろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶やかんだ。その後ご猫にもだいぶ逢あったがこんな片輪かたわには一度も出会でくわした事がない。のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙けむりを吹く。どうも咽むせぽくて実に弱った。これが人間の飲む煙草たばこというものである事はようやくこの頃知った。

C# で再び圧縮する

PHP で圧縮したデータと異なる文字列になりましたが…780 バイト。果たして大丈夫なのか。

H4sIAAAAAAAEAG1Ui3LaMBD8dZ94JBRoCAl5NjUpiXnWTh9JCTXkY86S7L/o3gmHmbYzjEZYp7vdvT3Zwa7M52xO
mHpMGVMTq+8tmaZMhk2XI2MHfdvp6+mOaeyb9xIWGaY50xki/XnMBtdjpi7TjA0+zsqka7fYnIa/TG0t8aAxK1wv
tiOpYkx50XQ3l1r6ig0xhRVfYteJJKbR5caEG/n7xv2YaHyia1xsUHcstSgrZ1eu/cKE63q6p6A0hSAgCWY77Wqt
pNhsqgsBrKnawMOUshkCvzASmkiFYGzwQ9i0pM9MUAz7OyWeudu1iFAnCTmL9VcEF+vIjxa+/+TMQhWDDsiD6ws/
S90lNio1gREy3KlQY60LVOlfmQUbZe7acNRyx6dYAdX1oWQQFg06BnHfQhiamFQT5ETFPbty/m1fTmXZUxOCqJui
X5KZMm1NfSVqKHeAhyABJNTo2ONHlz47GmgS2AZ9Ny6CIELtECwCAsPYbnOh8xE+QV8A7zuSAGSZY72VvVgoYfMq
jaaZ68EJ98X6A9OwPo0BT7Gh17DEiBvZfhUKsWui0JXaDEUD1CftHZIc/KwYUs08tU/wzGU52KKWaggiSa15Wt2f
H5wgmIGwKQQNfNL9v3MoFefDXfSmIxPMHKpjiNqHzl4bm1yLVsFyYuCZCKjI37kUvxU8tE2D/gdX+9UK7nIZ1INE
gXJSTlrVBDYYqZ6vTGFyb6C50BEKDzLX+zWMyU6/TMuLlc9f2EAEtBj2Cz5Ub+xw8VxfhqXWAlqEvVTRF/VzUBsx
Z3px4TtHZR5sgMcBlliKr9aR3aDFxh5tihyQIAvExKm0T6cYGXArPC8QHBpCHzjkRsGn/lOsY4Uw8AVsiLP0Cyqf
f2mSgzj1KAWCQsHPf2LV62LgMERMuIgBCevMt9ARPFmRZDZDO4CAJ9oyvHXyONjhVk7hWIJngDyxKVy3tDkmS0UQ
w0MEIOzVDkmrCdweIXnZBxfEw5b/vDm1RVWHjA0GGRUhMqrIU1CNGz5+fK/yBwT7QUe7BQAA

PHP で 3. を解凍する

そんな心配をよそに、全く同じ文章が復元される。

吾輩わがはいは猫である。名前はまだ無い。どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪どうあくな種族であったそうだ。この書生というのは時々我々を捕つかまえて煮にて食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌てのひらに載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始みはじめであろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶やかんだ。その後ご猫にもだいぶ逢あったがこんな片輪かたわには一度も出会でくわした事がない。のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙けむりを吹く。どうも咽むせぽくて実に弱った。これが人間の飲む煙草たばこというものである事はようやくこの頃知った。

PHP と C# の圧縮文字列の差について

どちらも ID 0x1f 0x8b のバイナリデータですが、ヘッダの Extra Flags が異なっていました。

XFL (eXtra FLags)

These flags are available for use by specific compression methods. The "deflate" method (CM = 8) sets these flags as follows:

XFL = 2 - compressor used maximum compression, slowest algorithm
XFL = 4 - compressor used fastest algorithm

RFC 1952 - GZIP file format specification version 4.3

そういえば、php では圧縮レベル 9、C# では CompressionLevel.Fastest を指定していましたね…。そのせいか。

こんな感じで同じ gzip とはいえ、状況により値が変化します。
個人的には、相互で圧縮・解凍できるのであれば、(値の違いは)それほど気にする事ではないと思います。

参考資料、サイト

本当に色々なやり方があって、PHP と C# 相互運用できるものになかなか巡り合えませんでした…(特に C#)。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA