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する:

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

Google APIのrequireも忘れずに:

// ./app.js
var google = require('googleapis');

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

// ./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オプションを追加する:

// ./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
}));

ルートを調整

// ./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} );
})
});

テンプレート調整

// ./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している場合は、大量のファイルを一気にアップロードすると、メモリオーバーフローエラーが出る可能性があるので、プロダクションの場合は別のアプローチを考えたのがおすすめ