LoginSignup
5
6

More than 5 years have passed since last update.

Go言語でGoogle Drive APIとGmail APIを使う方法メモ

Last updated at Posted at 2019-02-25

初めに

会社で業務自動化のためのツールをGo言語で作っており、その中でGoogle Drive APIとGmail APIを使う機会があったのですが、結構はまるところが多く苦労したため、ここに分かったことを記します。

尚、記事が長くならないよう、サンプルコードから例外処理は省いています。

OAuth2認証

両APIに共通するのが、OAuth2を用いた認証です。
公式ドキュメントのサンプルに簡単なサンプルが載っていますが、このやり方だといちいち認証用のurlを出力し、ユーザにそれを手入力でブラウザに表示させ、さらに認証コードをコンソールに打ち込んでもらわなければなりません。

他の言語(Pythonなど)であれば、自動でブラウザを開き認証をするフローがライブラリにより用意されているのですが、Goではありません。

そこで、以下の記事を参考に、認証用画面を自動で開き、認証後はローカルサーバにリダイレクトをするようにしました。

コマンドラインアプリで OAuth2 認証を使う際に使えるテクニック

上の記事を参考に作ったコードが以下です。

クリックしてコードを展開
func googleDrive(){
    // APIを有効化したときに作成されるファイルにアクセス
    b, _ := ioutil.ReadFile("credentials/credentials.json")
    config, _ := google.ConfigFromJSON(b, drive.DriveScope)
    // 認証が既に済んでいれば、存在するファイル。初めて起動する場合は、存在しない。
    tokFile := "credentials/token.json"
    f, _ := os.Open(tokFile)
    tok := &oauth2.Token{}
    err := json.NewDecoder(f).Decode(tok)
    // 認証がまだ済んでいない場合
    if err != nil {
        authCode := getToken(config)
        tok, err = config.Exchange(context.TODO(), authCode)
        if err != nil{
            fmt.Println(err)
        }
        f, _ := os.OpenFile(tokFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
        json.NewEncoder(f).Encode(tok)
    }
}

func getToken(config *oauth2.Config) string{
    // ローカルサーバの立ち上げ
    l,err := net.Listen("tcp","localhost:8989")
    if err != nil {
        return ""
    }
    defer l.Close()

    err = open.Start(config.AuthCodeURL("state", oauth2.SetAuthURLParam("response_type", "code token")))

    quit := make(chan string)
    go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        if req.URL.Path == "/" {
            w.Write([]byte(`<script>location.href = "/close?" + location.hash.substring(1);</script>`))
        } else {
            w.Write([]byte(`<script>window.open("about:blank","_self").close()</script>`))
            w.(http.Flusher).Flush()
            fmt.Println(req.URL.Query())
            quit <- req.URL.Query().Get("code")
        }
    }))
    return <-quit
}


はまりどころは、getToken関数の中の

err = open.Start(config.AuthCodeURL("state", oauth2.SetAuthURLParam("response_type", "code token")))

の部分です。

上で紹介した記事のサンプルコードではresponse_typetokenとしていますが、Google Drive API及びGmail APIはそれをサポートしていないため、エラーが出ます。code tokenとすることで認証コードを取得できるようになりました。

Google Drive API

特定フォルダ下のファイル一覧取得

公式ドキュメントのサンプルでは「マイドライブ」の中の10個を取得するプログラムが紹介されいますが、ある特定の親フォルダの下のすべてのファイル一覧を取得する方法は、以下です。

クリックしてコードを展開
func googleDrive(){
    // ~ OAuth2認証の部分(上と一緒なので省略)~
    client := config.Client(context.Background(), tok)
    service, _ := drive.New(client)
    r, _ := service.Files.List().Q("'親フォルダのID' in parents").Fields("nextPageToken, files(id, name)").Do()
    fileList := r.Files
}

service.Files.List()でいったん全ファイルの一覧を取得していますが、そのあとQ関数で絞り込みをしています。

この関数の引数ですが、ドキュメントにある通り、独特な書き方の文字列で絞り込み方法を指定します。

ファイル名、ファイルタイプ、最終閲覧日などでも絞り込みが行えるようです。

フォルダを新しく作成

アップロードの部分は、ほかの言語は公式サイトにサンプルコードが載っているにもかかわらずGoにはありませんでした。

フォルダを作成するコードは、以下です。

クリックしてコードを展開
func googleDrive(){
    // ~ OAuth2認証の部分(上と一緒なので省略)~
    newFolder := drive.File{}
    newFolder.Name = "新しいフォルダ名"
    newFolder.MimeType = "application/vnd.google-apps.folder"
    newFolder.Parents = []string{"親フォルダのID"}
    temp, _ := service.Files.Create(&newFolder).Do()

MimeTypeをapplication/vnd.google-apps.folderとすると、フォルダとして作成できます。

ExcelファイルをSpreadSheetとしてアップロード

以下が、ExcelファイルをSpreadSheetとしてアップロードするサンプルです。

クリックしてコードを展開
func googleDrive(){
    // ~ OAuth2認証の部分(上と一緒なので省略)~
    uploadFile, _ := os.Open("サンプルエクセルファイル.xlsx")
    fileMetaData := &drive.File{Name:"サンプルエクセルファイル.xlsx",MimeType:"application/vnd.google-apps.spreadsheet",Parents:[]string{"親フォルダのID"}}
    res,_ := service.Files.Create(fileMetaData).Media(uploadFile).Do()

MimeTypeをapplication/vnd.google-apps.spreadsheetとすることで、SpreadSheetとしてアップロードできます。

Gmail API

件名、本文の文字化け対策

公式ドキュメントのサンプルコードを写経すると、メール件名が日本語だと文字化けします。

こちらの記事を参考にすると、直りました。

クリックしてコードを展開
func sendMail(itemList items, fileId, meetingDate, fromDate, toDate, mailAddress string){
    // ~Google Drive APIのOAuth2認証と一緒なので、省略~
    client := config.Client(context.Background(), tok)
    service, err := gmail.New(client)
    temp := "From: 'me'\r\n" +
        "To: " + 
        "sample@gmail.com" + 
        "\r\n" +
        "Subject:" + "日本語の見出し" + "\r\n" +
        "\r\n" + "日本語の本文"
    msgISO2022JP, _ := toISO2022JP(temp)
    msg := []byte(msgISO2022JP)
    message := gmail.Message{}
    message.Raw = base64.RawURLEncoding.EncodeToString(msg)

    _, err = service.Users.Messages.Send("me", &message).Do()
    if err != nil {
        fmt.Printf("%v", err)
    }
}

func toISO2022JP(str string) ([]byte, error) {
    reader := strings.NewReader(str)
    transformer := japanese.ISO2022JP.NewEncoder()

    return ioutil.ReadAll(transform.NewReader(reader, transformer))
}

HTMLメールの送信

メールのContent-Typeを'text/html'にすると、HTMLタグが使えるようになります。

クリックしてコードを展開
func sendMail(itemList items, fileId, meetingDate, fromDate, toDate, mailAddress string){
    // ~Google Drive APIのOAuth2認証と一緒なので、省略~
    temp := "From: 'me'\r\n" +
        "To: " + 
        "sample@gmail.com" + 
        "\r\n" +
        "Subject:" + "件名" + "\r\n" +
        "MIME-version: 1.0;\r\n" + 
        "Content-Type: text/html; \r\n" +
        "\r\n" + "本文"
    msgISO2022JP, _ := toISO2022JP(temp)
    msg := []byte(msgISO2022JP)
    message := gmail.Message{}
    message.Raw = base64.RawURLEncoding.EncodeToString(msg)}


GoでHTMLメールを送信する方法について調べると、Content-Type: text/html; charset: UTF-8を指定する、というものが出てきますが、charset:UTF-8とすると文字化けが起こりました。

Shift-JISなどを指定しても起こるので、とりあえず何も指定しないことにしたところ、うまくいきました。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6