dropbox-sdk-jsを使ってフォルダ内アイテムの共有リンクを取得

はじめに

最近フロントエンドの勉強をしています。 今作っているWebアプリで、Dropboxに保存してある写真を表示するという機能を作りたかったのですが、サーバサイドでAPIを叩き個別に取得して表示させるととても画面レスポンスが遅くなってしまいました。
そこでJavascriptで非同期に取得し、順次画面表示させてユーザーの体感速度を早くしようとしたのですが、そこで少しハマってしまったので解決方法を備忘として残しておきます。

dropbox-sdk-jsの導入

まずはdropbox-sdk-jsを導入します。 今回はnpmではなくCDN版のものを使用します。
Getting started | Dropbox JavaScript SDK

以下をhtmlに記載します。

<script src="https://unpkg.com/dropbox/dist/Dropbox-sdk.min.js"></script>
<script src="https://unpkg.com/dropbox/dist/DropboxTeam-sdk.min.js"></script>

フォルダ内アイテム一覧を取得

まずは共有リンクを取得したい画像アイテムのリストを取得します。 filesListFolderメソッドにフォルダのパスを渡せば取得出来るみたいです。

var dropbox_api_token = "((dropbox_api_token))";
var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token });

var dir_path = "path/to/items"
dbx.filesListFolder({path: dir_path})
  .then(function(response) {
    console.log(response.entries)
  })
  .catch(function(error) {
    console.error(error);
  });

これはチュートリアルに載っているコードそのままで取得できます。 これでresponse.entriesにアイテム一覧が配列で取得できました。

forループでフォルダ内のアイテムそれぞれの共有リンクを取得

一覧が取得できたので、それぞれのアイテムの共有リンクを取得しようと思います。 本来なら、共有リンクを作成dbx.sharingCreateSharedLinkWithSettingsの後に共有リンクを取得dbx.sharingListSharedLinksする流れですが、自分の環境だとサーバサイドで共有リンクの作成は完了しているので、リンク取得のみを記述します。

こんな感じです。

var dropbox_api_token = "((dropbox_api_token))";
var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token });
var entries;
var urls = [] //共有リンクの配列

var dir_path = "path/to/items"
dbx.filesListFolder({path: dir_path})
  .then(function(response) {
    console.log(response.entries)
    entries = response.entries;
    for(var i = 0;i<entries.length;i++) {
      dbx.sharingListSharedLinks({path: entries[i].path_display})
      .then(function(response) {
        urls.push(response.links[0].url);
      })
    }
  })
  .catch(function(error) {
    console.error(error);
  });

///////////////////////////////////////
//以下に共有リンクの配列(urls)を取得した後の処理を記載
///////////////////////////////////////

JavaScript初心者だった自分は、なんとなくこれで動作しそうだなーと思って、こんなコードを書いたのですが、これが全然うまくいきませんでした。
共有リンクが取得できている前提で後の処理を書いても「urlsの中身が空だよー」とエラーになってしまいます。

再帰的に関数を呼び出す

非同期処理が入っているため順番がめちゃくちゃになってしまうようなので、きっちりと順番順番に処理をしてもらうために、以下のように関数を再帰的に呼び出して処理することにしました。

var dropbox_api_token = "((dropbox_api_token))";
var dbx = new Dropbox.Dropbox({ accessToken: dropbox_api_token });
var urls = [] //共有リンクの配列

var dir_path = "path/to/items"
dbx.filesListFolder({path: path})
  .then(function(response) {
    // 関数の呼び出し
    getSharingLinks(0, urls, response.entries);
    $("#loading-img").fadeOut('slow');
  })
  .catch(function(error) {
    console.error(error);
  });

// 関数を定義
function getSharingLinks(i, urls, entries) {
  dbx.sharingListSharedLinks({path: entries[i].path_display})
    .then(function(response) {
      var url = response.links[0].url.replace("www.dropbox.com","dl.dropboxusercontent.com").replace("?dl=0","");
      urls.push(url);
      i++;
      if ( i == entries.length) {
        return console.log(url);
      } else {
        // 再帰的に関数を呼び出し
        getSharingLinks(i, urls, entries);
      }
    })
    .catch(function(error) {
      console.error(error);
    });
}

まとめ

Javascriptで非同期の処理を扱う際は、Promiseやasyncなどを使うのが基本みたいですが、ちょっと調べただけではよく理解できなかったので、今回はきれいではないですがとりあえずこんな感じで解決しました。 いつか必要になると思うのでasyncについてもいつか勉強しようと思います。