Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
5
Help us understand the problem. What is going on with this article?
@m2u

Cloud Storage にアップロードされたZIPファイルを自動展開する for Firebase

More than 1 year has passed since last update.

概要

Firebaseにおいて、Cloud StorageへZIPファイルがアップロードされたとき、それを自動展開するための方法です。

複数のファイルをまとめてアップロードしたいけど参照は個別にできたほうがうれしい…といった場合などに。
ちょっとした処理なのですが意外とスニペットが見付からなかったので記事にしました。

手順

Cloud FunctionsのCloud Storageトリガーを利用します。
Cloud Functionsの設定と初期化、デプロイなどの基本的な手順についてはスタートガイドを参照してください。
https://firebase.google.com/docs/functions/get-started?hl=ja

プロジェクトの初期化後、 index.js を次のように書き換えます。

index.js
'use strict'

const functions = require('firebase-functions')
const mkdirp = require('mkdirp-promise')
const gcs = require('@google-cloud/storage')({keyFilename: 'service-account-credentials.json'})
const unzipper = require('unzipper')

const path = require('path')
const os = require('os')
const fs = require('fs')

/**
 * バケットで新しいオブジェクト(または既存オブジェクトの新しい世代)が正常に作成された場合に送信されます。
 * 既存のオブジェクトをコピーまたは再作成した場合にも送信されます。
 * アップロードが失敗した場合、このイベントはトリガーされません。
 */
exports.extractZip = functions.storage.object().onFinalize((object) => {

  // ZIPファイル以外は対象外
  if (!object.name.endsWith('.zip') || !object.contentType.includes('zip')) {
    console.log(`処理対象外のファイルです。name=${object.name},contentType=${object.contentType}`)
    return null
  }

  const baseName = path.basename(object.name, path.extname(object.name))
  const srcFile = gcs.bucket(object.bucket).file(object.name)
  const tmpDir = path.join(os.tmpdir(), baseName)
  const tmpFile = path.join(os.tmpdir(), path.basename(object.name))
  const dstBucket = gcs.bucket('gs://example') // 展開先のバケット名
  const dstDir = path.join('extract', baseName) // 展開先のディレクトリパス

  return mkdirp(path.dirname(tmpFile))
  .then(() => {
    return srcFile.download({destination: tmpFile})

  }).then(() => {
    console.log('Cloud StorageからローカルへZIPファイルをダウンロードしました。', tmpFile)

    return fs.createReadStream(tmpFile)
      .pipe(unzipper.Extract({path: tmpDir}))
      .on('close', () => {console.log('ZIPファイルを次のディレクトリへ展開しました。', tmpDir)})
      .promise()

  }).then(() => {
    let uploads = []
    fs.readdirSync(tmpDir).forEach(file => {
      uploads.push(
        dstBucket.upload(path.join(tmpDir, file), {destination: path.join(dstDir, file)})
      )
    })
    return Promise.all(uploads)

  }).then(() => {
    console.log('ローカルからCloud Storageへ展開後ファイル群をアップロードしました。', dstBucket.name)

    fs.unlinkSync(tmpFile)
    fs.readdirSync(tmpDir).forEach(file => {
      fs.unlinkSync(path.join(tmpDir, file))
    })
    fs.rmdirSync(tmpDir)
    console.log('ローカルの一時ファイルを削除しました。')
    return null

  }).then(() => {
    // TODO: 後続処理のキックなど
    return null
  })
})

必要となるパッケージを npm で追加します。次がdependenciesの例です。

package.json
...
"dependencies": {
  "@google-cloud/storage": "^1.7.0",
  "firebase-admin": "~5.12.1",
  "firebase-functions": "^1.0.3",
  "mkdirp": "^0.5.1",
  "mkdirp-promise": "^5.0.1",
  "unzipper": "^0.9.2"
},
...

また、デプロイの前に、Cloud Storageへのアクセス権限を持つ認証情報ファイルを index.js と同じ functions ディレクトリへ格納してください。
上の例ではファイル名を service-account-credentials.json としています。

Firebaseサービスアカウントをそのまま用いる場合、管理コンソールの 設定 > サービス アカウント > 新しい秘密鍵の生成 より認証情報ファイルをダウンロードできます。

:warning: ガイドに記載されている通り、Cloud Functions for Firebaseは2018年7月現在ベータ版での提供のため、SLAの対象になりません。ご利用の際は十分ご注意ください。

5
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
m2u
しなやかに伸び縮みするタイプ。
kronos-jp
AI開発・WEB開発・システム開発・Android開発・iOS開発・IT研修・トレーニング・新入社員研修などを行う企業です。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
5
Help us understand the problem. What is going on with this article?