BOXのダウンロードの流れとSDK無しでダウンロードする方法


BOXからファイルをダウンロードする時にSDKを使わないと少し戸惑うのでメモ。


## 大まかな処理の流れ

利用するAPIの説明はこれです。
https://ja.developer.box.com/reference/get-files-id-content/

このAPIを叩くと、レスポンスが302で帰って来る時は、レスポンスヘッダーで`Location`が戻されます。
この`Location`には、`https://dl.boxcloud.com/d/1/b1!V1LhT0hCN....` のようなURLが入ってきます。
この`Location`から実際にファイルをダウンロードします。

レスポンスは2パターンあって、ステータスコードが`302`の場合は、ヘッダーに`Location`、`202`の場合は、ヘッダーに`Retry-After`が戻され、クライアントがファイルをダウンロードできるようになるまでの秒数を示します。

ためしても`202`がもどされなかったので、`202`が発生して、`Retry-After`が戻される時、curlとかaxiosではどういう挙動になるのか不明です。
[ここ](https://curl.se/docs/manpage.html#--retry)に、curl 7.66.0から`Retry-After`に従うようなことがかいてあるけど、どうなんだろう。試してないので動くかわかんないです。

SDKにはこのあたりの処理が間違いなくちゃんと入ってます。
オートメーションとか連携に使うなら、アクセストークンの取り回しなどもあるので、SDKで（特にJWT認証で）使うのがいいでしょうね。


## APIをcurlで実行


#### ダウンロード用URLの取得

まず、Locationをフォローしないかたちで（`-L`を付けずに）以下のように実行してみます。

```sh
curl -i -X GET "https://api.box.com/2.0/files/690849498295/content" -H "Authorization: Bearer XXXXXXXXXX"
```

以下のようなレスポンスが帰ってきます。

```
HTTP/1.1 302 Found
Date: Wed, 16 Dec 2020 02:28:43 GMT
Transfer-Encoding: chunked
Strict-Transport-Security: max-age=31536000
Cache-Control: no-cache, no-store
BOX-REQUEST-ID: 17d4c6d940827c69342f2705eb8df260c
Location: https://dl.boxcloud.com/d/1/b1!V1LhT0hCNCBXXWBMjANGzmCxSWNA0BsyXMMYGOAdG7ZSlBs4FzPrGPjDASb06VFsGc (めちゃくちゃ長いので省略) jLux9WOM./download
Connection: close
```

ふむふむ、`Location`が入ってます。



#### ダウンロード用URLまで続けて実行

curlでは、`-L` を付けて実行すると、上のレスポンスヘッダーのLocationに入ってきたURLに自動的に移動してくれるので、ヘッダーからLocationを取り出すような操作は不要です。

```sh
curl -i -X GET "https://api.box.com/2.0/files/690849498295/content" -H "Authorization: Bearer XXXXXXXXXX" -L
```

以下が帰ってきます。

```
HTTP/1.1 200 OK
Date: Wed, 16 Dec 2020 06:32:08 GMT
Content-Type: image/png
Content-Length: 86195
Connection: keep-alive
Accept-Ranges: bytes
Cache-Control: private
X-Envoy-Upstream-Service-Time: 116
Content-Disposition: attachment;filename="baseball_girl.png";filename*=UTF-8''baseball_girl.png
X-Robots-Tag: noindex, nofollow
Encryption_Policy_Id: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000

Warning: Binary output can mess up your terminal. Use "--output -" to tell 
Warning: curl to output it to your terminal anyway, or consider "--output 
Warning: <FILE>" to save to a file.

```

`-L`をつけると、一気にファイルを含んだレスポンスが帰ってきます。
`--output`つけてファイルに出力しろって警告してますね。
ファイルに出力するんだったら、`-i`で詳細情報だすのは不要です。



#### ファイルに保存

`-i` を外し、`-o/--output` で、出力先のファイルを指定すると、ファイルを直接保存可能になります。
APIに対して1発でファイルをダウンロードするコマンドは以下のようになります。

```
curl -X GET "https://api.box.com/2.0/files/690849498295/content" -H "Authorization: Bearer XXXXXXXXXX" -L -o sample.png
```

これで一気にファイルがダウンロードできました。



## Node.js（Axios）からの実行

Node.jsから実行してみるとこんな感じです。
axiosをつかっていますが、デフォルトで1つめのレスポンスに含まれるLocationをフォローしてくれますね。
ここでは`responseType`に`stream`を指定して、そのままWriteStreamに書き出してます。

```js
const axios = require("axios");
const fs = require("fs");
const accessToken = "XXXXXXXXXX";

axios
  .get("https://api.box.com/2.0/files/690849498295/content", {
    headers: { Authorization: `Bearer ${accessToken}` },
    responseType: "stream",
  })
  .then((result) => {
    const output = fs.createWriteStream("sample.png");
    result.data.pipe(output);
  })
  .catch((error) => {
    console.log(error.message);
  });

```


## Box node SDKを使う

ちなみに、Box Node SDKをつかうとこんな感じです。
わざわざアクセストークンを貼るなどせず、JWT認証をつかって認証周りの制御もやってます。
自動化するなら[Box公式SDK](https://ja.developer.box.com/sdks-and-tools/#%E5%85%AC%E5%BC%8Fsdk%E3%81%8A%E3%82%88%E3%81%B3cli)をつかうのが一番らくでしょうね。


```js

const boxSDK = require("box-node-sdk");
const config = require("./config.json");
const fs = require("fs");
const sdk = boxSDK.getPreconfiguredInstance(config);
const client = sdk.getAppAuthClient("enterprise");

client.files.getReadStream("690849498295", null, function (error, stream) {
  if (error) {
    return; // handle error
  }
  const output = fs.createWriteStream("./sample.png");
  stream.pipe(output);
});

```