VBScriptでgzipを利用する

# はじめに

Web APIとの通信時のトラフィックを削減して通信の速度や料金を抑えるための方法としてgzip圧縮があります。

VBScriptではWinHTTPを用いてgzip圧縮されたデータを受信することは出来ますが、解凍する手段がサポートされておらず、そのままではgzipを利用することが出来ません。

本記事では.NET FrameworkをCOM経由で利用することで、VBScriptでもgzipを利用する方法について記載します。

# 手順

#### 解凍処理実装

.NET FrameworkのGZipStreamクラスを用いて以下のように実装します。

```c#:C#
using System;
using System.Runtime.InteropServices;
using System.IO;
using System.IO.Compression;

namespace GZipCOM
{
    [Guid("xxxxxxxx-xxxx-xxxx-xxx-xxxxxxxxxxxx")]
    public interface IGZip
    {
        String GZipDecompressToUTF8(byte[] bytes);
    }

    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("GZipCOM")]
    [Guid("xxxxxxxx-xxxx-xxxx-xxx-xxxxxxxxxxxx")]
    public class GzipStream : IGZip
    {
        //GZip解凍
        public String GZipDecompressToUTF8(byte[] bytes)
        {
            var buffer = new byte[10240];

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

                            oMS.Write(buffer, 0, readSize);
                        }
                    }
                    //byteArray→string変換
                    String decompressString = ConvertByteArrayToString(oMS.ToArray());

                    return decompressString;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} Exception caught.", e);
                return null;
            }

        }

        //byteArray→string変換メソッド
        public String ConvertByteArrayToString(byte[] bytes)
        {
            return System.Text.Encoding.UTF8.GetString(bytes);
        }

    }
}
```

` GZipCOM ` がVBScriptでCreateObjectする際の名称になります。

```c#:
   [ProgId("GZipCOM")]
```

` GZipDecompressToUTF8 ` がVBScriptから呼び出すメソッド名になります。

```c#:
    public interface IGZip
    {
        String GZipDecompressToUTF8(byte[] bytes);
    }
```

byte配列ではVBScriptに正常に値を返せないため、Stringに変換しています。

```c#:
    //byteArray→string変換
    String decompressString = ConvertByteArrayToString(oMS.ToArray());
```

#### COM作成、登録

COMの作成、登録の方法については他にまとめられている記事がありましたのでそちらを紹介させて頂きます。

- [VBAまたはVBSからCOM経由で使用できる.NETのライブラリの作成方法 - Qiita](https://qiita.com/mima_ita/items/efcd1a6ea86f09047984)
- [C# で COM オブジェクトを作ってみる - Qiita](https://qiita.com/tomochan154/items/1ce33f2aef167c0fed9d)

※：大変参考になりました。ありがとうございます。

#### VBScript実装

リクエストヘッダに以下を設定してgzip通信を有効にします。

```
    WinHttp.SetRequestHeader "Accept-Encoding", "gzip, deflate"
```

作成、登録したCOMのオブジェクトを生成します。

```
    Dim GZipCOM : Set GZipCOM = CreateObject("GZipCOM")
```

Web APIにSend後、返却されたレスポンスを作成したメソッドで解凍します。
解凍した値がVBScript上で参照できていれば正常に動作しています。

```
    response = GZipCOM.GZipDecompressToUTF8(WinHttp.ResponseBody)
```


# 効果

私の環境ではネットワーク監視ツールでinbound traffic（受信データ）を確認したところ、半分程削減されており、正常にgzipが効いていることが確認出来ました。

gzip未使用
<img width="274" alt="gzip_before.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385323/e978fcec-e9f0-099b-8de2-1a5b912a527f.png">

gzip使用（グリーン部分がinbound traffic）
<img width="272" alt="gzip_after.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385323/8d674c37-940a-e8ca-5d5b-8f2200ca4454.png">

# さいごに

中々この方法にたどり着くことが出来ず諦めかけていましたが、分かってしまえば以外と簡単にgzipを利用することが出来ます。
.NET FrameworkのCOM経由の利用については一部インターフェースに使えない型があったり、本番環境への適用は十分な検証が必要になるなどいくつかハードルはありますが、標準ライブラリだけで実現出来ない場合の手段の一つとして検討してみても良いかもしれません。