【Go】AWS SDK GoのGetObjectの返り値が16進数になった...!

どーも!

たかぽんです!

今回はGo言語で開発をしていて、S3からファイルの情報を取得する際に困ったので、それについて書いていきます...!

何があった?

まずは簡単に何があったのかをお伝えしますね。

やっていることは簡単で、lambdaでGo言語でS3からファイルサイズ等の情報を取得していこうと思ってたんです。

コード例をみると、以下のような形です。

	obj, err := s3Svc.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(sourceBucketName),
		Key:    aws.String(sourceObjectKey),
	})
	if err != nil {
		logger.Error(err)
		return err
	}

	logger.Info("video size: ", obj.ContentLength)
	logger.Info("video type: ", obj.ContentType)

ただ、残念ながら上記ではうまくいかないんですね...

実際にここで出しているログの出力は以下のような感じになりました...

2021-06-21T11:26:29.776+09:00	{"level":"info","msg":"vide size: 0xc000028668","time":"2021-06-21T02:26:29Z"}
2021-06-21T11:26:29.776+09:00	{"level":"info","msg":"vide type: 0xc00008d330","time":"2021-06-21T02:26:29Z"}

本来はファイルサイズやファイルタイプが入るべきところに"0xc000028668"や、"0xc00008d330"といった数字がはいっているんですね。

んー・・・。

実は筆者が学生時代に習ったことがあるため、0x始まりは16進数ということは理解できたんです。

なので、最初はそのまま16進数でファイルサイズが出てきているのかな?と思ったんですが、いやいやまて..

ファイルタイプは流石に数字じゃないから16進数ではないだろう...と...

まぁ、念の為S3上で見れるファイルサイズと前述のログに出た16進数を10進数に変換した値をみても全然一致しないので...これはないなと...

それでは、他に16進数...?となると、アドレスかな・・・?という結論にいたり、それが当たりだったようです。

というのも、Go言語にはポインタという概念があり、(最も有名なのはC言語でしょうか?)そのポインタではデータの実態が、あるメモリに格納されていて、ポインタ自体はその格納されているメモリアドレスを指す値になります。

そのため、たくさんの箱があって、ポインタによってどの箱かを指していたりします。

その"どの箱”かを表すために先程のアドレス(16進数)が使われています。

そして、アドレスの実体を指す場合は、前述のコードを以下のように書き換えればOKです。

	obj, err := s3Svc.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(sourceBucketName),
		Key:    aws.String(sourceObjectKey),
	})
	if err != nil {
		logger.Error(err)
		return err
	}

	logger.Info("vide size: ", *obj.ContentLength)
	logger.Info("vide type: ", *obj.ContentType)

変えたのは、以下二行だけです。

	logger.Info("vide size: ", *obj.ContentLength)
	logger.Info("vide type: ", *obj.ContentType)

取得してきたオブジェクトから各値を使用する際に"*"、つまり、アスタリスクをつけてあげればOKです。

これで、"obj.ContentLength = 0xc000028668 が表す格納先にあるファイル..."という意味になります。

もしアスタリスクがなければ、"obj.ContentLength = 0xc000028668"ここまでで終わってしまいます。

これが原因でうまく値が取れていなかったんですね。

実際にログとしてだしてみて、何かしら16進数等が表示されてしまう場合は、ポインタじゃないか確認するといいかもしれないですね...!

次に、GoのAWS SDKの公式リファレンスから追いかける方法も念の為みておこうかなと思います。

Go AWS SDK 公式リファレンスから上記を理解する

さて、今回使ったGoのAPIにも公式リファレンスがあり、もちろん、GetObjectにも解説はあるんです。

ただ、結構開発知識ある前提で書かれているので、筆者のようなペーペーの初心者には難しい...

ので、自分なりに理解してみます...!

最も大切なのが以下ですね。

func (c *S3) GetObject(input *GetObjectInput) (*GetObjectOutput, error)

関数GetObjectがあり、この関数はS3という構造体に対して作られたメソッドになります。

S3という構造体は、リンクを押してみにいくと...

type S3 struct {
    *client.Client
}

こんな感じでした。

さらにexampleを見ると...

mySession := session.Must(session.NewSession())

// Create a S3 client from just a session.
svc := s3.New(mySession)

// Create a S3 client with additional configuration
svc := s3.New(mySession, aws.NewConfig().WithRegion("us-west-2"))

こんな感じです。

つまり、このS3という構造体はSession情報を渡すことで、S3関連の操作をするためのクライアントを作成できるわけですね。

そして、以下のように作成すれば、あとは認証情報等も毎回入力する必要なく(初回のセッション情報には必要)S3のAPI呼び出しができるようになります。

svc := s3.New(mySession)

このsvcから、先程のGetObjectをよ美出せることは以下でわかります。

func (c *S3) GetObject...

レシーバ引数にS3へのポインタがありますね。

レシーバ引数とは、このGetObjectが受け取る構造体を表すんですが、構造体の中の値を用いたメソッドを作成した場合、その構造体をレシーバ引数として渡し、追加の引数があればそれも受け取り...といった形になります。

わかりやすくすると以下のような感じですね。

func (<レシーバ引数>) <関数名>([引数]) [戻り値の型] {
    [関数の本体]
}

そのため、前述した

	obj, err := s3Svc.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(sourceBucketName),
		Key:    aws.String(sourceObjectKey),
	})

このコードでは、s3Svcという名前のS3構造体からGetObjectを呼び出すことで、レシーバ引数にs3Svcが渡されることになります。

そして、追加の引数には&s3.GetObjectInput...となっていますね。

それに対応するのは以下です。

(input *GetObjectInput)

ここでは引数として、GetObjectInput構造体のポインタを渡してあげる必要があります。

ポインタなので、先程の16進数の番地情報を渡す必要があるわけです。

ある構造体や変数の番地情報をだすためには、"&"をつけてあげればOKです。

そのため、再掲しますが...

	obj, err := s3Svc.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(sourceBucketName),
		Key:    aws.String(sourceObjectKey),
	})

ここの引数では、s3.GetObjectInputの構造体にバケット名、キー名を設定し、その構造体のポインタを渡している...ということになります。

そして本題の返り値ですね。

(*GetObjectOutput, error)

二つ値があり、errorも返してくれます。

そして、GetobjectOutputのポインタが帰ってきますね。

ポインタが帰ってくる...ということは、先程いったアドレスの番地が帰ってくるわけです。

その値の実態にアクセスするためには*でアクセスしなければちゃんと値が取れないことは先にお話しした通りです。

これで先程のGetObjectの理解が進みましたね...!

もっと細かい解説等も書かれていますが、あくまで公式リファレンスの読み方をなぞっていく形で読みたかったので、そこは割愛します...!

上記のように他のメソッドも追いかけてみると少しわかるようになるんじゃないかなと思います...!

まとめ

今回はGoのAWS SDKを使っていて出会った謎の16進数について調べました。

筆者の場合は大学時代にポインタを使うC言語を触っていたのでなんとかたどり着けましたが、もしポインタとかをあまり理解せずにGoとか書いている人では結構難しそうな内容ですよね...

というかいまだにポインタへの苦手意識は強いです...(学生時代もポインタ系課題には泣かされました...w)

Goを触るようになってきたので、改めて勉強しなおしたいなぁと感じました...

それでわ!

おすすめの記事