Webサーバーから送信するデータをgzip圧縮するGo言語コード

現代ではスマートフォンからのインターネットアクセスが大幅に増えていますよね。しかしスマートフォンは有線で繋ぐパソコンほど高速な通信は出来ませんし、通信データ容量も限られています。

サーバー側のネットワークトラフィック量にも限界があり、1つ1つの通信に使用するデータ量を減らさなくてはなりません。その方法の1つとしてデータをgzip圧縮するのがあります。

gzip圧縮するとしないでは、データ量が倍くらいに変わることも少なくありませんので、Webサーバーを運用する際には是非取り入れたい仕組みです。

当記事では、サーバーからデータを送信する際にgzip圧縮する方法をご紹介します。

コンテンツの転載は固くお断りいたします。

gzip圧縮して送信するコード

以下にそのコードをご紹介します。なお、このコードは『Go言語でシンプルで簡単なHTTPサーバーの作り方』や『Go言語で複数ドメインにも対応可能なHTTPSサーバーの作り方』の記事で紹介したサーバープログラムの延長線上にある機能です。

上記の記事で書いている内容は省いて説明していきますので、分からないことがあれば上記の記事を見れば解決するかもしれません。


package main

import (
	"bytes"
	"compress/gzip"
	"log"
	"net/http"
	"strings"
)

func handler(w http.ResponseWriter, r *http.Request) {
	buff := []byte("

Hello!

") if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { gz := new(bytes.Buffer) ww := gzip.NewWriter(gz) if _, err := ww.Write(buff); err != nil { log.Println(err) return } if err := ww.Close(); err != nil { log.Println(err) return } w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) if _, err := gz.WriteTo(w); err != nil { log.Println(err) return } } else { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) if _, err := w.Write(buff); err != nil { log.Println(err) return } } }

コードの解説

では、上記のコードを部分ごとに解説していきます。

必要なパッケージを指定する


package main

import (
	"bytes"
	"compress/gzip"
	"log"
	"net/http"
	"strings"
)

bytes パッケージ

バイトデータのバッファーを操作するのに使用します。

compress/gzip パッケージ

名前の通り、gzip圧縮に関連するパッケージです。

strings パッケージ

文字列操作に関するパッケージです。

そのほかのものは他の記事でもご紹介している通りです。

送信する元となるデータを用意


buff := []byte("

Hello!

")

送信する元となるデータはバイトのスライスです。文字列としてHTMLを用意しました。

クライアントがgzipに対応しているかチェック


if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {

まず『http.Request』である『r』から『Header.Get』関数を使って『Accept-Encoding』HTTPヘッダを取得します。そして『strings』パッケージの『Contains』関数を使って、ヘッダに『gzip』の文字列が含まれているかチェックします。

Accept-Encoding ヘッダは『GTmetrix』でも確認が可能であり、実際に確認したものが以下の画像です。

赤色の枠で囲まれている部分がそうであり『gzip』の文字列も含まれていますね。これはクライアントがサーバーに接続する際に送るヘッダであり、『gzip』が含まれていると gzip圧縮に対応しているということです。

今回は、gzipに対応していれば圧縮し、していなければ圧縮せずにそのままデータを送信しています。

バッファーとライターを作成する


gz := new(bytes.Buffer)
ww := gzip.NewWriter(gz)

『new(bytes.Buffer)』で新しくバイトのバッファーを作成しており、『gzip』パッケージの『NewWriter』関数に先ほどのバッファーを指定してライターを新規作成します。

データを書き込んで圧縮する


if _, err := ww.Write(buff); err != nil {
    log.Println(err)
    return
}

ライターに『Write』関数を使ってデータを書き込みます。データを書き込むと同時にgzip圧縮されます。圧縮済みのデータはバッファーに保存されています。

ライターをクローズする


if err := ww.Close(); err != nil {
    log.Println(err)
    return
}

書き込んだらライターを『Close』関数を使ってクローズします。クローズしないと正常にデータが書き込まれない場合があります。

また、圧縮済みデータを後でキャッシュしたりして利用したい場合は、クローズの後に『gz.Bytes()』として『[]byte』を取得しておいて下さい。

必要なHTTPヘッダを送信する


w.Header().Set("Content-Encoding", "gzip")
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)

『http.ResponseWriter』である『w』に『Header().Set』関数を使ってHTTPヘッダをセットしていきます。

まず『Content-Encoding』ヘッダに『gzip』と書き込んで、送信したデータがgzip圧縮されていることを知らせます。そして『Content-Type』には今回はHTMLデータなので『text/html』をセットしておきます。

最後に、『WriteHeader』関数に『http.StatusOK』をセットしてヘッダを送信します。

圧縮済みデータを送信する


if _, err := gz.WriteTo(w); err != nil {
    log.Println(err)
    return
}

バッファーの『WriteTo』関数を使って圧縮済みデータを送信します。第一引数に『w』を指定してデータの送信先を指定します。

クライアントがgzip圧縮に対応していなければそのまま送信する


w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)

if _, err := w.Write(buff); err != nil {
    log.Println(err)
    return
}

先ほどの送信方法とそれほど変わらないコードですが、『w』の『Write』関数を使ってデータを送っておきます。

終わりに

以上でコードの紹介はおしまいです。

本来、圧縮処理というのは複雑な処理ですが、Go言語には豊富な標準ライブラリがありますし、そのおかげで簡単にgzip圧縮することが出来るようになっています。

パフォーマンスも悪くないと思いますので、皆さんも是非利用してみて下さいね。