express.jsでファイルをGoogle Cloud Storageにアップロードする(2)

ChainZ(クリエイター)
いろいろやってます。

この記事はhttp://befool.co.jp/blog/chainzhang/expressjs-file-upload-to-google-cloud-storage-1/の引き続きになる。

Google Cloud Storage

Google Cloud Storageを使うには、Google Cloudに登録するのが必要。詳しくはGoogle Cloudの公式サイトへ:https://cloud.google.com/

登録完了したら、My Consoleへ、新規プロジェクトdemo

新規が完了したら、Cloud Storageのページへ。新規したプロジェクトはまだバケットがないので、下記の画面が表示される:

「バケットを作成」をクリックして、upload-demoというバケットを作成する:

バケットとは?
簡単に言えば、ファイルのスコープみたいなもんで、サイトのドメインごとにバケットを作成することが多い。

自分のアプリでGoogle Cloud Storageを利用するには、Google API認証用の情報が必要。メニュー「APIと認証」の「認証情報」というページにアクセスし、「新しいクライアントIDを作成」をクリックして認証情報を作成する:

ここで、サービスアカウントを選んでください。作成ボタンをクリックすると情報がJSONファイルとしてダウンロードされる。

普段Google APIはoauth2.0で認証を行う。しかし、そのAPIを利用するのがアプリの場合は(Server to Server)、サービスアカウントのほうが直接で簡単。

express.js + multerでGoogle Cloud Storageにファイルをアップロード

先ほどダウンロードしたJSONファイルをexpress.jsのプロジェクトディレクトリーに移して、requireする:

1
2
3
// ./app.js

var google_auth_info = require('./demo-xxxxxxxxxx');

Google APIのrequireも忘れずに:

1
2
3
// ./app.js

var google = require('googleapis');

これで準備完了、まずGoogle APIの認証をしてみる:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ./app.js

// JWTはJSON Web Tokenの意味。
var authClient = new google.auth.JWT(
 // クライアントemail
  google_auth_info.client_email,
  null,
  // プライベートキー
  google_auth_info.private_key,
  // スコープ
  ['https://www.googleapis.com/auth/devstorage.read_write']
);

// 認証開始
var google_auth;
authClient.authorize(function(err, tokens) {
  if (err) {
    // エラーの場合は処理を中断し、エラーを表示する
    console.log(err);
    return;
  }
  google_auth = authClient;
  console.log('-- Google APIの認証通った!');
});

npm startして、認証が通った場合は、コンソールが下記のようなメッセージが表示されるはず:

認証はちゃんと通ったので、Google APIを利用して、ファイルをGoogle Cloud Storageにアップロードする機能を実装しよう:

multerの設定を調整する:

multerにinMemoryオプションを追加する:

1
2
3
4
5
6
7
8
9
10
11
12
// ./app.js

app.use(multer({
  // inMemoryをtrueにすると、アップされたfileはBuffer形式として、file.bufferに格納される。
  inMemory : true,
  onFileUploadComplete: function(file, req, res) {
    console.log('UP完了:' + file.path);
  },

  // putSingleFilesInArrayをtureにすると、ファイル一個しか選択されても配列として扱う
  putSingleFilesInArray: true
}));

ルートを調整

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
43
44
45
46
47
48
// ./app.js

// アップロードフォームを表示する
// + next引数追加
app.get('/upload', function(req, res, next) {
  // Google API認証まだ通ってない場合、「準備中」エラーを表示
  if (! google_auth) return next(new Error('準備中'));
  res.render('upload');
});

// アップロード処理
app.post('/upload', function(req, res) {
  // Google Cloud Storageにアップされたファイルを格納用
  var files = [];

  // アップされたファイル
  async.eachSeries(req.files.files, function(f, done){
    storage.objects.insert({
      // プロジェクトID
      project: 'swift-casing-93005',
      // バケット名
      bucket : 'upload-demo',
      // ファイル名
      name : f.name,
      // アクセス権限
      predefinedAcl : 'publicread',
      // ファイル情報
      media : {
        // 中身
        body: f.buffer,
        // mimeタイプ
        mimeType : f.mimetype
      },
      // 認証情報、これがないと、エラーになる
      auth: authClient
    }, function(err, result){
        if (err) return done(err);
        console.log(result);
        // アップロード完了したら、urlを定義する
        f.url = 'https://storage.googleapis.com/upload-demo/' + f.name;
        files.push(f);
        done();
    });
  }, function(err){
    if (err) return next(err);
    res.render('uploaded', {files : files} );
  })
});

テンプレート調整

1
2
3
4
5
6
7
// ./views/uploaded.jade

extends ./layout

block content
  for file in files
    img(src=file.url, style="width:200px;height:auto;margin-right:2px;float:left;border:1px solid #DFDFDF;")

これで、実装完了、早速プレビューしてみる:

できた! ちなみに、アップされたファイルはGoogle Cloud Consoleからも確認できる:

まとめ

この記事ではexpress.jsでGoogle Cloud Storageにファイルをアップロードするアプリを作ったが、どれも必要最低限の実装になって、実際の運用にはまだたくさんの工夫をしなければならない

multerは便利だが、inMemoryオプションをtrueしている場合は、大量のファイルを一気にアップロードすると、メモリオーバーフローエラーが出る可能性があるので、プロダクションの場合は別のアプローチを考えたのがおすすめ