Movable Type の Data API を使って、画像をアップロードする

Movable Type の Data APIを利用してアプリ開発などを行う場合、画像をアップロードしたい、という場面もあると思います。

Data APIから画像をアップロードする方法について、サンプルコードを書いてみました。

## やること

* Movable Type の Data API を使って、画像をアップロード。Movable Type に登録する。
* JavaScript で実装する
* jQuery、Movable Type のJavaScript SDK を利用する。

## 検証環境

* Movable Type 6.3.3
* Google Crhome, Microsoft Edge


## 画像をアップロードする

以下のような流れで考えてみました。

1. JavaScript SDKで認証を行う
2. html5のFile API で画像のサムネイル表示
3. 画像をアップロードしたいパスを入力
4. アップロードボタンを押すことで、画像をサーバー上にアップロードし、MTのアイテムとして登録する

認証およびMTへの登録は Data API、入力値の反映などはjQueryで行います。

mt-image-upload.html

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>画像をMTに登録するサンプル</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
  <script src="<$MTStaticWebPath$>data-api/v3/js/mt-data-api.js"></script>
</head>
<body>

<div class="container" id="container">

  <h2>画像をMTへ登録するサンプル</h2>

        <div id="message"></div>

        <div id="content" style="display: none;">
        <p><input type="file" name="upload" id="fileToUpload"></p>
        <p>アップロードパス</p>
        <p><input type="text" name="path" id="path">

        <p><button id="uploadFile">MTへアップロード</button></p>

        </div>

</div>

<script>

// Movable Type Data API のインスタンス生成

const api = new MT.DataAPI({
  baseUrl: "<$MTCGIPath$><$MTDataAPIScript$>",
  clientId: "image-upload"
});

// MTのブログIDを変数 siteId に格納

const siteId = <$MTBlogID$>;

// メッセージ表示のために使う変数 [html] を定義

let html = '';

$(function() {

  // MTにサインインしているかどうか、トークンを確認する。
  // トークンがない場合、サインインを促す。存在した場合、hiddenで隠されている登録用フォームを表示する

  if (!api.getTokenData()) {
    let html = `
          <p>画像を登録するためにはサインインしてください。</p>
          <p><input type="button" id="signin" class="btn btn-primary btn-lg" value="サインイン"></p>
          `
    $("#message").html(html);
  } else {
    $("#content").show();
  }

  // サインインをクリックしたら、SDKの関数 [getAuthorizationUrl] を実行して、サインインを行う

  $("#signin").on("click", function() {
    location.href = api.getAuthorizationUrl(location.href);
  });

  // アップロードするファイルを選択

$('input[type=file]').after('<p><span id="image"></span></p>');

  $('input[type=file]').change(function() {
    const file = $(this).prop('files')[0];

    // 画像以外は処理を停止

    if (!file.type.match('image.*')) {
      $(this).val('');
      $('span').html('');
      return;
    }

    const reader = new FileReader();
    reader.onload = function() {
      const img_src = $('<img height=\"300\">').attr('src', reader.result);
      $('span').html(img_src);
    }
    reader.readAsDataURL(file);
  });

  // 登録ボタンをクリックしたら、アップロード用の関数[upload]を実行する。

  $("#uploadFile").on("click", function() {
    upload();
  });

  // 画像をアップロードしてMTに登録する。エラーになったら、レスポンスデータからエラーメッセージを表示。それ以外は成功メッセージを表示

  function upload() {
      const data = {
        site_id: siteId,
        path: $("#path").val(),
        file: $("#fileToUpload")[0].files[0],
        autoRenameIfExists: true,
        normalizeOrientation: true,
      };
      api.uploadAsset(data, function(response) {

        if (response.error) {
          alert(response.error.message);
          return;
        }

         alert("画像をアップロードしました。");
         return;
    });
  }

});
</script>

</html>
```


## 画像をアップロードするときに、画像の説明やタグも合わせて登録する

画像の情報に説明やタグなどを加えたい場合は、以下のような流れになります。

1. JavaScript SDKで認証を行う
2. html5のFile API で画像のサムネイル表示
3. 画像をアップロードしたいパスを入力
4. アップロードボタンを押すことで、画像をサーバー上にアップロードし、MTのアイテムとして登録する
5. アップロード後に取得したレスポンスデータから、登録された画像のアイテムIDを取得する
6. [PUT]メソッドで、アイテムIDの情報に、説明とタグを加えてupdateする

4から5にデータを渡すためには、非同期通信の処理を待って次の処理へ渡す必要があります。今回はJavaScriptのPromise メソッドを使って、アップロードに対するレスポンスデータを引き渡しました。

Promise に関しては、[こちらの記事](http://qiita.com/TakeshiNickOsanai/items/14bcf1a17c4996fad0dc) をご参照ください。

タグの情報は、カンマ区切りで入力します。カンマごとに語句を分割して配列[tags]に格納後、Data API に渡しています。

アイテムをアップデートするためのエンドポイント [update](https://www.movabletype.jp/developers/data-api/v3-reference.html#assets-assets-put) を、JavaScript SDKで叩きます。

mt-upload-image-1.html

```html

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>タグや説明を付加した画像をMTに登録するサンプル</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
  <script src="<$MTStaticWebPath$>data-api/v3/js/mt-data-api.js"></script>
</head>
<body>

<div class="container" id="container">

  <h2>タグや説明を付加した画像をMTへ登録するサンプル</h2>

        <div id="message"></div>

        <div id="content" style="display: none;">
        <p><input type="file" name="upload" id="fileToUpload"></p>
        <p>アップロードパス</p>
        <p><input type="text" name="path" id="path">
        <p>画像の説明</p>
        <p><input type="text" name="desc" id="desc">
        <p>画像のタグ</p>
        <p><input type="text" name="tags" id="tags">

        <p><button id="uploadFile">MTへアップロード</button></p>

        </div>

</div>

<script>

// Movable Type Data API のインスタンス生成

const api = new MT.DataAPI({
  baseUrl: "<$MTCGIPath$><$MTDataAPIScript$>",
  clientId: "image-upload-1"
});

// MTのブログIDを変数 siteId に格納

const siteId = <$MTBlogID$>;

// メッセージ表示のために使う変数 [html] を定義

let html = '';

$(function() {

  // MTにサインインしているかどうか、トークンを確認する。
  // トークンがない場合、サインインを促す。存在した場合、hiddenで隠されている登録用フォームを表示する

  if (!api.getTokenData()) {
    let html = `
          <p>画像を登録するためにはサインインしてください。</p>
          <p><input type="button" id="signin" class="btn btn-primary btn-lg" value="サインイン"></p>
          `
    $("#message").html(html);
  } else {
    $("#content").show();
  }

  // サインインをクリックしたら、SDKの関数 [getAuthorizationUrl] を実行して、サインインを行う

  $("#signin").on("click", function() {
    location.href = api.getAuthorizationUrl(location.href);
  });

  // アップロードするファイルを選択

$('input[type=file]').after('<p><span id="image"></span></p>');

  $('input[type=file]').change(function() {
    const file = $(this).prop('files')[0];

    // 画像以外は処理を停止

    if (!file.type.match('image.*')) {
      $(this).val('');
      $('span').html('');
      return;
    }

    const reader = new FileReader();
    reader.onload = function() {
      const img_src = $('<img height="300">').attr('src', reader.result);
      $('span').html(img_src);
    }
    reader.readAsDataURL(file);
  });

  // 登録ボタンをクリックしたら、関数[upload]を実行。非同期通信のレスポンスが返ってきたら、次の関数[update]に値を引き渡し、タグと説明データを加える。

  $("#uploadFile").on("click", function() {
    upload().then(function(response) {
      update(response);
    });
  });

  // 画像をアップロードしてMTに登録し、レスポンスデータを Promise で渡す

  function upload() {
    return new Promise((resolve, reject) => {
      const data = {
        site_id: siteId,
        path: $("#path").val(),
        file: $("#fileToUpload")[0].files[0],
        autoRenameIfExists: true,
        normalizeOrientation: true,
      };
      api.uploadAsset(data, function(response) {
        if (response.error) {
          alert(response.error.message);
          reject;
        }
        resolve(response);
      });
    });
  }

  // 関数 upload() から返ってきたレスポンスデータを元に、タグと説明のデータを追加してアップデートする

  function update(response) {
    const assetId = response.id;
    const tag = $("#tags").val();
    const tags = tag.split(',');
    const extData = {
      description: $("#desc").val(),
      tags: tags,
    };
    api.updateAsset(siteId, assetId, extData, function(result) {
      if (result.error) {
        alert("アイテムの情報をアップデート中にエラーが発生しました。");
        return;
      }
      alert("画像をアップロードしました。");
      return;
    });
  }
});
</script>

</html>

```

出力したhtmlで画像を登録する画面。

![upload-image-1.png](https://qiita-image-store.s3.amazonaws.com/0/83767/c8428778-3c6c-c807-fc6d-3f756c00b792.png)


登録後の画像。タグや説明が付加されています。

![upload-image-2.png](https://qiita-image-store.s3.amazonaws.com/0/83767/9858cacb-8b03-f783-c936-dba7b94499b2.png)