【NCMB】ファイルストアを使ってみよう（前編） with Monaca

# はじめに

本チュートリアルを実践するとこんなアプリが作れます：
<img alt="IMG_8281.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/284861bf-4a49-e3a3-7d92-e83d5a950218.png" width="300px">

カメラで撮影した画像をサーバー（mobile backend）にアップロード＆データベースで管理するサンプルアプリを作成するチュートリアルです。本前半ではファイル及び情報の保存まで行い、後半で保存したデータを取得して画面に表示します。

* 所要時間目安： 動作確認するだけなら15分～20分程度

後編はこちらをご覧ください↓
[【NCMB】ファイルストアを使ってみよう（後編） with Monaca](https://qiita.com/natsumo/items/5f5425ea3fcd62331cb4)

# 動作環境

* PC
   * Windows でも Mac でもOK
   * ブラウザ「Google Chrome」を利用しますのでインストールが必要です
* 開発プラットフォーム「Monaca」または「Monaca Education」
   * アカウント作成が必要です ＞ 下記URLより登録（Freeプランあり）
      * Monaca https://monaca.mobi/ja/signup
      * Monaca Education https://monaca.education/ja/signup
* クラウドデータベース「ニフクラ mobile backend」
   * アカウント作成が必要です ＞ 下記URLより登録（Freeプラン（SNSアカウントのみ）あり）
      * https://console.mbaas.nifcloud.com/signup
* 動作確認端末
   * Android でも iOS でもOK
   * デバッガーアプリ「Monaca」または「Monaca for Study」のインストールが必要です ＞ 下記URLより登録（無料）
      * Monaca https://ja.monaca.io/debugger.html
      * Monaca for Study https://edu.monaca.io/faq/debugger

# 作業手順

次の２段回に分けてアプリを作成していきます。

1. [カメラで撮影した画像をサーバーに保存する（ファイルストアへの保存）](/natsumo/items/7e5b02e40bf954e374cc#1-カメラで撮影した画像をサーバーに保存するファイルストアへの保存)
1. [サーバーに保存した画像に情報を持たせて管理する（データストアへの保存）](/natsumo/items/7e5b02e40bf954e374cc#2-サーバーに保存した画像に情報を持たせて管理するデータストアへの保存)

## 1. カメラで撮影した画像をサーバーに保存する（ファイルストアへの保存）

### 環境準備

* ブラウザ「Google Chrome」を開き、mobile backend にログインします
   * https://console.mbaas.nifcloud.com/
* アプリ名を入力して「新規作成」をクリックして mobile backend にアプリを作成します
   * アプリ名 例）「FileDemoApp」
   * 既にアプリが１つ以上存在する場合は「新しいアプリ」をクリックして作成する
      * （参考） https://mbaas.nifcloud.com/doc/current/introduction/div_quickstart_javascript_monaca.html#アプリの新規作成
* アプリが発行されたら「OK」ボタンをクリックします
   * 発行されたAPIキーは後で利用します
* 管理画面（ダッシュボード）が表示されます
* mobile backend の管理画面はそのままにして、ブラウザ「Google Chrome」で別のタブを開きます
* Monaca にログインします
   * Monaca https://console.monaca.mobi/
   * Monaca Education https://console.monaca.education/
* 「新しいプロジェクトを作る」＞「最小限のテンプレート」をクリックして Monaca にプロジェクトを作成します
   * 例）プロジェクト名「ファイルデモアプリ」
* 作成したプロジェクトを開き、プロジェクトに mobile backend を Monaca から利用するための JavaScript SDK を導入します
   * 「設定」＞「JS/CSSコンポーネントの追加と削除...」をクリックします
   * 「JS/CSSコンポーネントの追加と削除」画面で「ncmb」と入力して「検索」をクリックします
   * 「ncmb」が表示されるので「追加」をクリックします
   * 「JS/CSSコンポーネントのインストール」アラート画面が出るので「インストール」をクリックします
      * バージョンはデフォルトで最新版が選択されていますので、操作は不要です
   * 「読み込むファイルを選択してください」と出るので「components/ncmb/ncmb.min.js」にチェックを入れ「保存」をクリックします
   * 一覧に「ncmb」が表示されれば導入完了です
* アプリのカメラ機能を利用するために必要な Cordovaプラグイン「cordova-plugin-camera」を有効化します
   * 「設定」＞「Cordovaプラグインの管理...」をクリックします
   * 「利用可能なプラグイン」の中から Camera「cordova-plugin-camera」を探し、にカーソルを合わせます
   * ポップアップが表示されるので「有効」をクリックします
   * 「有効なプラグイン」として表示されれば有効化完了です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/f31a87dd-7e7c-9908-5a80-779b67cd0db0.png)

準備が整ったので、ここから実装作業に入ります。

### 画面（HTML）の実装

カメラを起動するボタン（buttonタグ）の設置と、撮影した画像を表示する場所（imgタグ）を用意します。

* index.html の bodyタグに以下を記述する
   * 既存の `This is a template for Monaca app.` は削除してOK

```html:index.html
<div>
    <button onclick="takePicture()">写真を撮る</button>
</div>
<img id="photo" src="" style="max-width: 80%; max-height: 400px;">
```

### 処理（JavaScript）の実装

Monaca から mobile backend を利用するための処理（APIキーの設定とSDKの初期化）を実装します。

* index.html の scriptタグに以下を記述する

```js:index.html
var applicationKey = "YOUR_APPLICATION_KEY";
var clientKey = "YOUR_CLIENT_KEY";
var ncmb = new NCMB(applicationKey, clientKey);
```
* `YOUR_APPLICATION_KEY` 及び `YOUR_CLIENT_KEY` には [準備](#準備) で mobile backend にアプリを作成した際に発行されたAPIキー（アプリケーションキー及びクライアントキー）に貼り換えます
   * APIキーを確認するには、ブラウザで mobile backend の管理画面を開いているタブを開き、右上の「アプリ設定」＞「基本」にある「APIキー」で確認できます
      * （参考） https://mbaas.nifcloud.com/doc/current/introduction/div_quickstart_javascript_monaca.html#アプリの新規作成 
   * 「コピー」ボタンでそれぞれコピーし、ブラウザで Monaca プロジェクトのタブに戻り、貼り付けてください

「写真を撮る」ボタンを押したときの以下動作（関数`takePicture()`）を実装します。

1. カメラを起動して、撮影
1. 撮影後、撮影した画像をmobile backend にアップロード

ファイルのアップロード時には以下の注意点があります。

> * カメラで撮影した写真データは「Base64形式」で取得していますが、ファイルストアに写真を保存する場合はBlob形式に変換する必要がる
> * ファイルストアに格納するファイル名は一意である必要がある（重複した場合上書きされる仕様）

上記踏まえ、Base64からBlobへ変化する関数及びファイル名としてユニークIDを作成する関数も用意しています。

* index.html の scriptタグ内末尾に以下を追記する

```js:index.html
function takePicture() {
    var options = {
        sourceType: Camera.PictureSourceType.CAMERA,
        destinationType: Camera.DestinationType.DATA_URL
    };
    
    navigator.camera.getPicture(onSuccess, onError, options);
    
    function onSuccess(imageData){
        // ncmbに画像をアップロード
        var fileName = makeUUID() + ".jpg";
        var fileData = toBlob(imageData, "image/jpeg");
        ncmb.File.upload(fileName, fileData)
            .then(function(result){
                alert("画像アップロード成功");

                // 撮影した画像を画面に表示
                var image = document.getElementById("photo");
                image.src = "data:image/jpeg;base64," + imageData;
                
                // ★2.でコードを追加します
            })
            .catch(function(error){
                alert("画像アップロード失敗" + error);
            });
    }
    // 撮影キャンセル
    function onError(message){
        alert("エラー: " + message);
    }
}

// Blobを作成
function toBlob(base64, mime_type) {
    var bin = atob(base64.replace(/^.*,/, ''));
    var buffer = new Uint8Array(bin.length);
    for (var i = 0; i < bin.length; i++) {
        buffer[i] = bin.charCodeAt(i);
    }
    
    try{
        var blob = new Blob([buffer.buffer], {
            type: mime_type
        });
    }catch (e){
        return false;
    }
    return blob;
}

// UUID生成
function makeUUID() {
    var d = + new Date();
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
        .replace(/[xy]/g, function(c) {
            var r = (d + Math.random() * 16) % 16 | 0;
            return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
        });
}
```

#### コード解説

Cordovaプラグイン「cordova-plugin-camera」を利用してカメラ起動と撮影：

```js
// オプション設定
var options = {
    sourceType: Camera.PictureSourceType.CAMERA,   // 撮影モードを指定
    destinationType: Camera.DestinationType.DATA_URL   // 画像の取得形式（Base64形式）を指定
};
// カメラを起動
navigator.camera.getPicture(onSuccess, onError, options);
// 撮影後の処理
function onSuccess(imageData){

}
// 撮影キャンセル時の処理
function onError(message){

}
```

mobile backend への画像アップロード：

```js
// ファイル名をユニークIDで作成
var fileName = makeUUID() + ".jpg";
// ファイルデータをBase64からBlobへ変換して設定
var fileData = toBlob(imageData, "image/jpeg");
// ファイル名とファイルデータを指定して mobile backend へアップロード
ncmb.File.upload(fileName, fileData)
    .then(function(result){
        // アップロード成功時の処理
    })
    .catch(function(error){
        // アップロード失敗時の処理
    });
```

### 動作確認

動作確認端末でデバッガーアプリ「Monaca」または「Monaca for Study」を起動し
該当プロジェクトを開きます。

* 「写真を撮る」ボタンをタップします

<img alt="IMG_8276.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/0d1614c6-7afd-a579-c4d5-577d4dfc6ca1.png" width="300px">

* 写真を撮影し「写真を使用」をタップします

<img alt="IMG_8277.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/73c5230a-330b-cd10-12fd-a9b8db0c9b4d.png" width="200px">

* 少し待つ（サーバーへ画像をアップロードするのでファイルサイズが大きい場合は数秒かかります）と「画像アップロード成功」とアラートが表示されるので「OK」をタップします

<img  alt="IMG_8278.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/0e7e5828-415e-d1ba-460c-1733085cb2dd.png" width="250px">

* アップロードされた画像が画面に表示されます

<img alt="IMG_8279.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/5cfbfe19-0ec3-bbea-b5a2-21a9c33e8324.png" width="250px">

サーバー（mobile backend）に画像が正しく保存されたかどうか確認します。
PCに戻り、ブラウザで mobile backend の管理画面を開いているタブを開きます。

* 左のメニューバーから「ファイルストア」をクリックします
* 一覧に１件ファイル画像ファイル（jpg）が保存されていることが確認できます
* 画像をクリックすると右側に表示される「ダウンロード」をクリックすると画像が確認出来ます

<img alt="IMG_8282.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/5ee066d4-da01-6ed4-2545-3b0b4c9e5e2a.png" width="600px">

## 2. サーバーに保存した画像に情報を持たせて管理する（データストアへの保存）

アップロードした画像を管理するには情報を添えてデータストアを活用する必要があります。
ファイル名と共に、情報をデータストアに格納してファイル管理ができるようにします。
ブラウザで Monaca プロジェクトを開いているタブを開きます。

### 画面（HTML）の実装

ファイルのアップロードに成功したら動的に画像の情報（タイトルと画像の説明）の入力欄及び登録ボタンを作るための場所（divタグ）をそれぞれ用意します。

* index.html の bodyタグ内末尾に以下を追記する 

```html:index.html
<div id="title"></div>
<div id="comment"></div>
<div id="btn"></div>
```

### 処理（JavaScript）の実装

ファイルアップロード成功時のコールバック内で情報入力欄及び登録ボタンを生成します。

* index.html の scriptタグ内、`// ★2.でコードを追加します` の下に以下を追記する 

```js:index.html
// タイトルと画像の説明の入力欄を作成
var title = document.createElement("input");
title.id = "titleData";
title.type = "text";
title.placeholder = "タイトル";
var comment = document.createElement("textarea");
comment.id = "commentData";
comment.placeholder = "画像の説明";
// 画像情報の登録ボタンの生成
var sendBtn = document.createElement("button");
sendBtn.onclick = saveData(fileName);
sendBtn.innerHTML = "情報を登録する";
// 画面に追加
document.getElementById("title").appendChild(title);
document.getElementById("comment").appendChild(comment);
document.getElementById("btn").appendChild(sendBtn);
```

「情報を登録する」ボタンを押したときの動作（関数`saveData()`）を実装。
タイトルと画像説明の入力値を取得して、先に保存したファイル名と共にデータストアの「FileData」クラスに格納。

* index.html の scriptタグ内末尾に以下を追記する 

```js:index.html
function saveData(fileName) {
    return function() {
        var title = document.getElementById("titleData").value;
        var comment = document.getElementById("commentData").value;

        // 入力チェック
        if (title === "" || comment === "") {
            return alert("未入力の項目があります")
        }

        var FileData = ncmb.DataStore("FileData");
        var fileData = new FileData();
        fileData.set("fileName", fileName)
            .set("title", title)
            .set("comment", comment)
            .save()
            .then(function(result){
                // 保存成功
                alert("画像情報保存成功")
            })
            .catch(function(error){
                // 保存失敗
                alert("画像情報保存失敗" + error);
            });
    }
}
```

### 動作確認

再び動作確認端末でデバッガーアプリ「Monaca」または「Monaca for Study」を起動し
該当プロジェクトを開きます（開いたままだった場合は更新をします）。
先ほどと同様に撮影後、「画像アップロード成功」アラートで「OK」をタップするところまで進めます。

* アップロードされた画像と一緒に画像の情報（タイトルと画像の説明）の入力欄及び登録ボタンが表示されます

<img alt="IMG_8279.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/d36bd9c7-d6db-5d10-f8ee-0c6d45e9051e.png" width="300px">

* 画像の情報（タイトルと画像の説明）を入力して「情報を登録する」ボタンをタップします

<img alt="IMG_8280.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/d4cabcc5-8e7c-6270-1e04-8d9317b93079.png" width="300px">

* 正しく保存されると「画像情報保存成功」とアラートで表示されます

<img alt="IMG_8281.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/284861bf-4a49-e3a3-7d92-e83d5a950218.png" width="300px">

サーバー（mobile backend）に画像データに紐づく情報が正しく保存されたかどうか確認します。
PCに戻り、ブラウザで mobile backend の管理画面を開いているタブを開きます。

* 左のメニューバーから「データストア」をクリックします
* 「FileData」クラスが作成されているのでクリックします
* ファイル名と一緒に画像のタイトルと説明が格納できていることが確認出来ます

<img alt="IMG_8283.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112032/e4dd596e-d873-564b-0ff6-8b84b6f2ddd8.png" width="600px">

# おわりに

本前編では撮影した画像及び情報をサーバーへ保存する流れを理解しました。後編では保存したファイル及び情報をサーバーから取得してアプリでの表示していきます。

後編はこちら↓
[【NCMB】ファイルストアを使ってみよう（後編） with Monaca](https://qiita.com/natsumo/items/5f5425ea3fcd62331cb4)