あらすじ
GoogleのIdentity PlatformやCloud Storageを使ったアプリ開発をしていると、複数のエンジニア間でデータの衝突や相違が起きることがある。例えばデータベースには登録されているが、Cloud Storageには登録されていないとかその逆とか、名前が重複しちゃったとか。
こういう場合ローカルの開発環境でエミュレーターを使うという方法があるのだが、Identity PlatformやCloud Storageは公式のエミュレーターが存在しない。
なので大抵の場合はFirebase Emulatorを使うことになる。
これについては firebase emulator docker
でググればそれなりに情報量の多いブログや記事が出てくるので参考にすればなんとかなる。
参照してもらうとわかるのだが、実はそんなにすんなり導入できるわけではなく、それなりにDockerの知識が要求される。
ということで、他にもう少し楽な選択肢はないかと検索すると、 fake-gcs-server というものが出てくる。今回はこの謎いエミュレーターでCloud Storageをエミュレートしてみる。
導入
以下の記事を参考に。
・
・
(その他の記述)
・
・
cloud-storage:
container_name: local-cloud-storage
image: fsouza/fake-gcs-server:latest
tty: true
ports:
- "4443:4443"
volumes:
- ./tmp/cloud-storage/data:/data
- ./tmp/cloud-storage/storage:/storage
command: -scheme http -public-host ${URL:-localhost}:4443
非常に簡単ではあるが、コマンド引数とvolumesの説明をする。
-
- ./tmp/cloud-storage/data:/data
例えば./tmp/cloudstorage/data
の下にtest
というディレクトリを作るとそれがバケットになる。 -
- ./tmp/cloud-storage/storage:/storage
アプリからファイルをアップロードするとこのstorageにファイルが溜まっていく。 -
-scheme http
httpでのアクセスを許可する -
-public-host ${URL:-localhost}:4443
localhost:4443 をホストに設定する
これらを総合すると、アップロードしたファイル(例えばtest.txt)には以下のURLでアクセスするとブラウザで表示orダウンロードできる。
http:localhost:4443/バケット名/test.txt
署名付きURLの発行
ようやく本題。公式ドキュメントには以下のように記述されている。
Using with signed URLs
It is possible to use fake-gcs-server with signed URLs, although with a few caveats:
- No validation is made on the query params (signature, expiration ...)
- You need your client to modify the URL before passing it around (replace storage.googleapis.com with something that points to fake-gcs-server)
- You need to configure fake-gcs-server to accept this local URL (by setting -public-host)
正直わけがわからないので実際に試した結果何が言いたいのかを超訳すると 署名付きURLはURLに署名するだけなんでアプリの機能じゃない。-public-host
に指定したURLで直接ファイルにアクセスしてちょ である。
TypeScript + Node.jsでの実装例
つまりこういうこと。
import { Storage } from '@google-cloud/storage';
import type { GetSignedUrlConfig } from '@google-cloud/storage';
// NODE_ENVがproduction以外の場合はfake-gcs-serverを使用する
const storage = process.env.NODE_ENV !== 'production'
? new Storage({
apiEndpoint: 'http://localhost:4443',
projectId: 'test',
})
: new Storage({
keyFilename: rocess.env.GOOGLE_APPLICATION_CREDENTIALS
projectId: process.env.GCP_PROJECT_ID,
});
const bucketName = process.env.GCS_BUCKET_NAME || 'your-bucket-name';
const getSignedUrl = async (filePath: string): Promise<string> => {
try {
// NODE_ENVがproduction以外の場合はローカルのURLをそのまま返す
if (process.env.NODE_ENV !== 'production') {
const signedUrl = `http://localhost:4443/${bucketName}/${filePath}`;
return signedUrl;
}
const [signedUrl] = await storage
.bucket(bucketName)
.file(fileName)
.getSignedUrl();
return signedUrl;
}
catch (error) {
throw error;
}
};
ドキュメントにも明確には載ってないけどこれでOKらしい。
ちなみに無理に署名付きURLを発行しようとすると以下のエラーが発生してしまう。
Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.