Box上でPDFファイル変換(Node.js編)

#概要
この記事は、@daichiiiiiiiさんの下記記事を参考にNode.jsで実装したものです。
リスペクト記事
> box上のオフィスファイルをPCにダウンロードせずにPDF変換する
> https://qiita.com/daichiiiiiii/items/12c2bb163fe013f400a9

#3/21 更新
チームに見せたところ、
**「ファイル上書きじゃなくて、別ファイルとしてアップロードするようにしてほしい」**
と言われたので、別ファイルとしてアップロードする方法に編集しました。

#環境構築
##Box
###Box Developer Account作成
1.　<https://developer.box.com/>へアクセスし、右上のメニューからSignUpする。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/41e5a82d-d421-f75e-f273-f411d2147a6a.png)
2.　確認メールが送られます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/db99d737-d2bf-f831-76d8-12cc641796e1.png)
3.　メールの確認をします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/646048bd-d328-5fda-a728-0ad11763430a.png)
4.　Boxのログイン画面が表示されるので、ログインします。
5.　設定が完了したら、Developerコンソールへアクセスします。
https://app.box.com/developers/console

###新規アプリケーションの作成
1.　[アプリの新規作成]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/664f64b5-c51f-37e7-c97b-140f03e9c7d2.png)
2.[カスタムアプリ]を選択して、[次へ]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/238d0bab-a6a8-6d31-def6-9920ea2668ff.png)
3.認証方法では、[標準OAuth2.0(ユーザー認証)]を選択して、[次へ]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/52eb42b1-7088-2e57-5d63-60fe1f516c39.png)
４.アプリケーションに適当な名前をつけて[アプリの作成]をクリックします。
５.DeveloperTokenが発行されますが、ここでは使わないので、[アプリの表示]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/eab9c19b-48ae-4a1a-eec0-81d0adc2999b.png)

###Web App Integrationの作成
1.作成したアプリケーションを開きます。
2.左のメニュー[統合]をクリックし、[新しいWebアプリ統合を作成]をクリックします。
必要な情報を入力していきます。
詳細な情報はBoxのドキュメントを参照してください。
> Box Developers > Docs > Web Application Integrations
>　https://developer.box.com/docs/box-web-application-integrations

- アプリ情報
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/ac7baebb-9606-3010-2f6f-3ec32940b749.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/1fa91057-89b9-6d33-1dd3-fe254f7ca193.png)

- コールバック設定
[クライアントコールバックのURL]は後に作成するHerokuのURLを入れるため、ここではダミーで<https://localhost>などとダミーの値を入れておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/01dfe7e6-4d0d-55bf-feea-51a7ae5c9f33.png)

- コールバックパラメータ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/4e1ae35d-b8bc-19fc-a862-b49fcee91fab.png)

- 認証
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/d966e531-7107-6be2-680c-bf7bd0bd9cc1.png)

- 統合ステータス
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/10022d57-eeb4-54b8-3730-14f78fc4f8f7.png)

全て設定が終わったら右上の[変更を保存]をクリックし保存します。


##Heroku
###Heroku環境構築
Herokuは日本語の資料がありましたので、こちらのサイトを参考にして環境構築をしてください。
> https://github.com/herokaijp/devcenter/wiki/quickstart

この時点では、「ステップ 3: ログイン」まで実施すれば大丈夫です。

###Herokuアプリケーション作成
1.HerokuCLIを使ってHerokuにログインします。

```
$ heroku login
Enter your Heroku credentials.
Email: <your email address>
Password (typing will be hidden): 
Logged in as <your email address>
```

2.Herokuでアプリケーションを作成します。
アプリケーション名は、Herokuの中で一意である必要があります。

```
$ heroku apps:create <アプリケーション名>
Creating ⬢ <アプリケーション名>... done
https://<アプリケーション名>.herokuapp.com/ | https://git.heroku.com/<アプリケーション名>.git　
```

3.Gitリポジトリを初期化します。

```
$ git init
```
4.リモートリポジトリとしてHerokuを追加します。

```
$ git remote add heroku https://git.heroku.com/<アプリケーション名>.git
```

5.Git管理対象外ファイル(.gitignore)を作成します。

```.gitignore
.DS_Store
.gitignore
npm-debug.log
node_modules
```
6.Herokuがプログラム起動する際に実行するファイル(Procfile)を作成します。

```Procfile
web: node server.js
```

### Herokuアプリケーションへ環境変数をセット
Boxの認証情報をHeroku環境変数としてセットしておきます。
セットする資格情報は下記の通りです。

| KEY              |補足                      |
|:-----------------|:------------------------|
| BOX_CLIENT_ID    |アプリのClient ID           |
| BOX_CLIENT_SECRET|アプリのClient Secret       |


Herokuへ環境変数を登録するコマンドは下記です。

```
$ heroku config:set　<KEY>="<VALUE>"
```

#プログラミング
次は、server.jsへコードを書き、デプロイします。

##コーディング
BoxのNode.js　SDKを利用します。
> https://github.com/box/box-node-sdk/

以下はサンプルプログラムです。
（あくまで動作を確認するためのサンプルなので、業務で使う場合はセキュリティ等の検討を十分に行ってください。
[実稼働環境におけるベスト・プラクティス: セキュリティー](http://expressjs.com/ja/advanced/best-practice-security.html))

```server.js
//Import Modules
var restify = require('restify');
var expressSession = require('express-session');
var BOX_CLIENT_ID = process.env.BOX_CLIENT_ID;
var BOX_CLIENT_SECRET = process.env.BOX_CLIENT_SECRET;
var Promise = require('bluebird');
var fs = require('fs');
var BoxSDK = require('box-node-sdk');
var async = require('async');
var bodyParser = require('body-parser');
var basicAuth = require('basic-auth-connect');
var helmet = require('helmet');

// Setup Restify Server
var server = restify.createServer();
server.use(bodyParser.urlencoded({ extended: true }));
server.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false }));
server.use(helmet());

server.listen(process.env.port || process.env.PORT || 3978, function () {
   console.log('%s listening to %s', server.name, server.url);
});

server.post('/api/box', function(req, res){
  var msg = 'PDFファイルに変換しています。変換が完了したら、新しいバージョンとして上書きします。' ;
  res.header('Content-Type', 'text/plain;charset=utf-8');
  res.send(msg);
    // Boxからのリクエストを受け取る
    // Boxで[PDF変換]メニューをクリックすると、BoxからPOSTで下記のパラメータが渡されます。
  var authCode = req.body.auth_code;
  var fileId = req.body.file_id;
  var fileExtension = req.body.file_extension;
  var fileName = req.body.file_name;
  var pdffilename = (fileName).replace('.' + fileExtension, '.pdf');

  var sdk = new BoxSDK({
    clientID: BOX_CLIENT_ID,
    clientSecret: BOX_CLIENT_SECRET
  });

  var tokenInfo = {};
  var pdfurl = "";

  var promise = new Promise(function(resolve, reject){ 
    // コールバックパラメーターのauth_codeを使ってAccessTokenを取得
    sdk.getTokensAuthorizationCodeGrant(authCode, null, function(err, res) {
      if(err){
        reject(err);
      }
      tokenInfo = res;
      console.log(tokenInfo);
      resolve();
    });
  });

  promise.then(function(){
    // Representations APIを使って、PDFファイルをダウンロード
    // https://developer.box.com/v2.0/reference#get-representations
    var client = sdk.getBasicClient(tokenInfo.accessToken);
    client.files.getRepresentationContent(fileId, client.files.representation.PDF)
      .then(function(stream) {
        // Boxからダウンロードしたファイルを一時的にHerokuサーバーの/tmp配下にダウンロード
        var output = fs.createWriteStream("/tmp/" + fileName);
        stream.pipe(output);
        output.on('close', function(){
          //ファイル情報から親フォルダIDを取得
          client.files.get(fileId, null, function(err, res){
            if(err){
              reject(err);
            }
            var folderId = res.parent.id;
            //新しいファイルをアップロード
            client.files.uploadFile(folderId, pdffilename, fs.createReadStream("/tmp/" + fileName), function(err, res){
              if(err){
                reject(err);
              }
            });
          });
        });
      });
  }).catch((err) => { // エラーハンドリング
    console.error(err);
  });
});
```

## デプロイ
Herokuへデプロイします。

```
$ git add . && git commit -m "<your message>" && git push heroku master
```

ログを確認する場合は、下記コマンドです。

```
$ heroku logs --tail
```

## Boxへ[クライアントコールバックのURL]をセット

1. BoxのDeveloperコンソールを開き、作成したアプリを開きます。
2. [統合]メニューの[コールバック設定]の[クライアントコールバックのURL]に、HerokuのアプリケーションURL+エンドポイント(https://<アプリケーション名>.herokuapp.com/api/box)を入力し、[変更を保存]します。

# テスト
[・・・]→[統合]→[PDF変換]をクリック！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/44127b44-ebba-bf53-55ce-72d64b99f6ae.png)

PDFファイルがアップロードされたようです！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/364ce7d8-7286-cbd4-229e-4435e6a406ca.png)

新しいファイルとしてPDFに変換されたファイルがアップロードされています！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/eccad478-7103-aee8-13c3-8a0c7baf3a53.png)


無事、PDF変換に成功しました！

この機能、業務で使えたら便利ですね。