LOULIZCategoryプライバシーポリシー

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

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

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

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

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

Contents

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

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

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

Go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

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

func handler(w http.ResponseWriter, r *http.Request) {
    buff := []byte("<p>Hello!</p>")

    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
        }
    }
}

コードの解説

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

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

Go
1
2
3
4
5
6
7
8
9
package main

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

bytes パッケージ

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

compress/gzip パッケージ

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

strings パッケージ

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

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

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

Go
1
buff := []byte("<p>Hello!</p>")

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

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

Go
1
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に対応していれば圧縮し、していなければ圧縮せずにそのままデータを送信しています。

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

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

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

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

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

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

ライターをクローズする

Go
1
2
3
4
if err := ww.Close(); err != nil {
    log.Println(err)
    return
}

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

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

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

Go
1
2
3
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』をセットしてヘッダを送信します。

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

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

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

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

Go
1
2
3
4
5
6
7
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圧縮することが出来るようになっています。

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


初心者でも簡単に高速で大規模なシステムを作れる『Go言語』のススメ

Go言語でシンプルで簡単なHTTPサーバーの作り方

Go言語で複数ドメインにも対応可能なHTTPSサーバーの作り方

Go言語でGCPのGCEからCloud SQLインスタンスへ接続する方法

Go言語でデータをキャッシュしたい時に便利な『go-cache』の使い方

Go言語でタイトルなどを動的に埋め込んでHTMLを出力するテンプレートの書き方

Markdown記法で書かれたものをHTMLに変換するGo言語コード

Go言語でシンプルなHTTPキャッシュヘッダーの作り方

画像をアルゴリズムを選んで簡単にすぐにリサイズするGo言語コードの紹介と解説

Cloud StorageからダウンロードとアップロードするGo言語コード

WWW無しへ、HTTPSなどへリダイレクトを行うGo言語コードの紹介と解説

GAEでGo言語製ウェブサーバーのためのapp.yamlの基本的な書き方

GAEで初めてのGo言語製ウェブサーバーの作り方

GAEのスタンダード環境でGo言語を使ってログを出力する方法

GAEのスタンダード環境からGo言語でDatastoreの基本操作を行う方法

Goプログラムの実行方法と画面に文字列を表示させよう![Go言語入門]

コードの中にコードの解説。コメントの書き方![Go言語入門]

基本的なデータの扱い方と種類[Go言語入門]

条件分岐、if、elseなどの使い方 [Go言語入門]

Go言語入門、そしてステップアップに最適なおすすめの本をご紹介!

Go言語での開発もこれで楽々!Visual Studio Code をご紹介!

GCPのロードバランサでクライアントがHTTPS接続かどうかを確認する方法

タグや要素、HTMLの基礎の解説【HTML・CSS入門】

AMP化HTML・CSSの作り方の基礎

アドセンスをAMPページでも表示する方法

CSSの超基礎を解説【HTML・CSS】

class属性とid属性【HTML・CSS入門】

軽量で多言語に対応、簡単に使えるシンタックスハイライト『Prism』

サイト作りが楽しく簡単になる、オススメの便利なサイトをいくつかご紹介