Fetch API をいろいろ試してみた。

#GET Text

Fetch API で GET メソッドを使ってサーバから文字列を受け取る例です。なお、関数 element は次のようになっています。

```js
function element(id) {
    return document.getElementById(id);
}
```

##クライアント側

```js
if (!form1.id.value) {  // id="form1" の "form1" の id="id" のテキストボックスの値をチェックする。
    alert('id が空欄です。');
    return;
}
fetch('/fetch/getPath/' + form1.id.value) // /fetch/getPath/:id へリクエストを行う。
.then(res => res.text())  // Response を受け取り res.text() のプロミスを返す。
.then(text => element("message").innerText = text)  // res.text() の結果 (text) を受け取り、id="message" というエレメントの内部文字列に設定する。
.catch(err => element("message").innerText = err.message); // エラーがあったら、id="message" というエレメントの内部文字列にエラーメッセージ設定する。
```

##サーバ側 (fetch.js)

サーバ側では Node.js + Express を使用しています。
この例では SQLite3 のテーブル Pictures にクエリーを行い、id (主キー) に対する path の値を取得しています。

```js
// id を指定して path を得る。
router.get("/getPath/:id", (req, res) => {
    const db = getDb();  // データベースオブジェクトを得る。
    const sql = "SELECT path FROM Pictures WHERE id=" + req.params.id;
    db.get(sql, (err, row) => {
        if (err) {
            res.send(err.message);  // エラーの場合は、エラーメッセージを返す。
            db.close();
        }
        else {
            if (row) {
                res.send(row.path);  // ここでクライアント側へ文字列 (row.path) を送っている。
                db.close();
            }
            else {
                db.send('');  // id に対応するパスがない場合は、空文字列を返す。
                db.close();
            }
        }
    });
});
```

#GET JSON

Fetch API で GET メソッドを使ってサーバから JSON 形式でデータを受け取る例です。

##クライアント側

```js
if (!form1.id.value) {  // id="form1" の "form1" の id="id" のテキストボックスの値をチェックする。
    alert('id が空欄です。');
    return;
}
fetch("/fetch/getProperty/" + form1.id.value)
.then(res => res.json())  // Response を受け取り、JSON データのプロミスを返す。
.then(data => element("message").innerText = JSON.stringify(data))  // JSON データを受け取り、文字列に変換して id="message" というエレメントの内部文字列に設定する。
.catch(err => element("message").innerText = err.message);  // エラーがあったら、id="message" というエレメントの内部文字列にエラーメッセージ設定する。
```

##サーバ側 (fetch.js)

```js
// id を指定してアルバム番号、タイトル、作者、メディア、識別マーク、情報を得る。
router.get("/getProperty/:id", (req, res) => {
    const sql = "SELECT album, title, creator, media, mark, info FROM Pictures WHERE id=" + req.params.id;
    const db = getDb();
    db.get(sql, (err, row) => {
        if (err) {
            res.json({'error':err.message});  // エラーがあったらエラーメッセージを返す。
            db.close();
        }
        else {
            if (row) {
                const data = {
                    'error':'',
                    'album':row.album,
                    'title':row.title,
                    'creator':row.creator,
                    'media':row.media,
                    'mark':row.mark,
                    'info':row.info
                };
                res.json(data);  // JSON データ (data) を返す。
                db.close();
            }
            else {
                res.json({'error':'id が正しくありません。'});  // id に対応するデータがない場合は、エラーメッセージを返す。
                db.close();
            }
        }
    });
});
```

#GET BLOB

Fetch API で GET メソッドを使ってサーバから BLOB データを受け取る例です。ここでの BLOB とは画像ファイルのような決まった形式のバイナリーデータです。(データベースの BLOB とは意味が異なる)

##クライアント側

```js
if (!form1.path.value) {  // id="form1" の "form1" の id="path" のテキストボックスの値をチェックする。
    alert('パスが空欄です。');
    return;
}
fetch("/fetch/getImage?path=" + form1.path.value)
.then(res => res.blob())   // Response を受け取り、BLOB データのプロミスを返す。
.then(blob => {  // BLOB データを id="picture" という img エレメントの src 属性にセットする。
    element('picture').src = window.URL.createObjectURL(blob);  // 引数で指定されたオブジェクトを表す URL を含む DOMString を生成する。
})
.catch(err => element("message").innerText = err.message);  // エラーがあったらエラーメッセージを表示する。
```

##サーバ側 (fetch.js)

```js
// path を指定して画像ファイル(JPEG/PNG)を得る。
router.get("/getImage", (req, res) => {
    const path1 = req.query.path.replace(/\\/g, "/");
    if (path1.endsWith('.jpg') || path1.endsWith('.JPG') || path1.endsWith('.png') || path1.endsWith('.PNG')) {
        res.sendFile(path1);  // 画像ファイルを返す。
    }
    else {
        res.status(500).send('');  // 画像ファイルでない場合はエラーを返す。
    }
});
```

#GET ArrayBuffer

Fetch API を使い GET メソッドで ArrayBuffer データを受け取る例です。ArrayBuffer も BLOB と同じバイナリーデータですが、BLOB と異なり形式の縛りがない純粋のバイト列です。この例では、バイト列 [48, 49, 50, 51, 52] を ArrayBuffer として受け取り文字列として表示します。

##クライアント側

```js
fetch('/fetch/getArrayBuffer')
.then(res => res.arrayBuffer())
.then(buffer => {
    let str = "";
    let bytes = new Int8Array(buffer);  // ArrayBuffer の内容をバイト列として処理する。
    for (let i = 0; i < bytes.length; i++) {
        str += bytes[i].toString() + ",";  // バイトデータを文字列表現に変換する。
    }
    element('message').innerText = str.substring(0, str.length -1);  // id="message" という要素の内部文字列に結果を表示する。(elementn の定義はページの先頭にて)
})
.catch(err => element("message").innerText = err.message);
```

##サーバ側 (fetch.js)

```js
// 固定 ArrayBuffer を返す。
router.get('/getArrayBuffer', (req, res) => {
    res.send(Buffer.from([48, 49, 50, 51, 52]));  // '0'-'4'
});
```

#POST JSON

Fetch API を使い POST メソッドで JSON データをサーバへ送り、結果 (実行した SQL) を受け取って表示する例です。

##クライアント側

```js
const URL = '/fetch/submit/Pictures';
// サーバへ送るデータを準備する。
let data = {
    id: form1.id.value,
    album: form1.album.value,
    title: form1.title.value,
    creator: form1.creator.value,
    path: form1.path.value,
    media: form1.media.value,
    mark: form1.mark.value,
    info: form1.info.value,
    fav: form1.fav.value,
    bindata: form1.bindata.value
};
// Content-Type は 'application/json' とする。
// body は data を文字列に変換したものとする。
let options = {method:"POST", headers:{'Content-Type': 'application/json'}, body:JSON.stringify(data)};
// fetch を行う。
fetch(URL, options)
.then(res => res.text())  // Response のプロミスから文字列のプロミスを取り出して関数値として返す。
.then(text => element("message").innerText = text)  // サーバから受け取った SQL が表示される。
.catch(err => element("message").innerText = err.message);
```

##サーバ側 (fetch.js)

```js
router.post("/submit/:table", (req, res) => {
    let tableName = req.params.table;
    let title = req.body.title ? req.body.title.replace(/'/g, "''") : "";
    let creator = req.body.creator ? req.body.creator.replace(/'/g, "''") : "";
    let info = req.body.info ? req.body.info.replace(/'/g, "''") : "";
    let path = req.body.path ? req.body.path.replace(/\\/g, "/").replace(/'/g, "''") : "";
    let db = getDb();
    let sql = "";
    // id が設定されていない場合は挿入とする。
    if (req.body.id) {
        sql = `UPDATE ${tableName}  SET album=${req.body.album}, title='${title}', creator='${creator}', path='${path}', media='${req.body.media}', mark='${req.body.mark}', info='${info}', fav=${req.body.fav}, bindata=${req.body.bindata} WHERE id=${req.body.id}`;
    }
    else {
        sql = `INSERT INTO ${tableName}  VALUES(NULL, ${req.body.album}, '${title}', '${creator}', '${path}', '${req.body.media}', '${req.body.mark}', '${info}', ${req.body.fav}, 0, ${req.body.bindata}, datetime('now', 'localtime'), 0)`;
    }
    // INSERT/UPDATE を実行する。
    let db = getDb();
    db.serialize(() => {
        db.run(sql);
        res.send(sql); // 実行した SQL を返す。
        db.close();
    });
});
```

なお、getDb() は SQLite3 データベースオブジェクトを取得する関数で次のように定義しています。

```js
// SQLite3 DB オブジェクトを返す。
function getDb() {
    let dbfile = (path.dirname(__dirname) + "/user.db").replace(/\\/g, "/");
    let db = new sqlite3.Database(dbfile);
    return db;
}
```

#POST FormData

Fetch API を使い POST メソッドで FormData オブジェクトをサーバへ送り結果を受け取る例です。

##注意事項

Express.js (ここで使用したのは v4.3.1) で POST データはミドルウェアで処理された後、post ハンドラに渡されます。以前のバージョンでは "body-parser" というミドルウェアが使用されていましたが、v4.3.1 では廃止されて express に統合されています。

この body-parser は JSON 形式のデータと input[type="submit"] により submit されたフォームデータにのみ対応していて、FormData オブジェクトには対応していないようです。(req.body が undefined になる)

そのため、FormData オブジェクトを処理できるミドルウェアをインストールしておく必要があります。ここでは ["express-form-data"](https://www.npmjs.com/package/express-form-data) というミドルウェアを使用しています。このミドルウェアは "multipart/form-data" に対応しているので、ファイルアップロード (input[type="file") も可能です。

このミドルウェアを利用するには、"npm i epress-form-data" によりインストールを行い、サーバ側モジュールの最初の方に次の2行を入れておく必要ああります。

```js
const formData = require('express-form-data');
router.use(formData.parse());
```

##クライアント側

```js
let data = new FormData(form1);
let options = {method:"POST", body:data};
fetch(URL, options)
    .then(res => res.text())
    .then(text => element("message").innerText = text)
    .catch(err => element("message").innerText = err.message);
```

##サーバ側 (fetch.js)

"POST JSON" と同じですが、express-form-data の対応が必要。(POST FormData の注意事項参照)

#POST FormData with Request

Fetch API を使い POST メソッドで Request オブジェクトを使用し、FormData オブジェクトをサーバへ送り結果を受け取る例です。

##クライアント側

```js
let data = new FormData(form1);
let options = {method:"POST", body:data};
let request = new Request(URL, options);
fetch(request)
.then(res => res.text())
.then(text => element("message").innerText = text)
.catch(err => element("message").innerText = err.message);
```

##サーバ側 (fetch.js)

"POST JSON" と同じですが、express-form-data の対応が必要。(POST FormData の注意事項参照)

#POST BLOB

Fetch API を使い POST メソッドで BLOB データをサーバへ送り結果を受け取る例です。id="canvas1" という canvas　 エレメントに描画されたデータを BLOB としてサーバへ送ります。

##クライアント側

```js
let canvas1 = element('canvas1');  // element() はこのページの先頭参照。(id に対応するエレメントを返す関数)
canvas1.toBlob((blob) => {  // canvas1 の描画内容を BLOB に変換する。
    blob.arrayBuffer().then(png => {  // さらに BLOB を ArrayBuffer (純粋なバイナリーデータ) に変換する。
        var ba = new Uint8Array(png);  // ArrayBuffer の内容をバイト列として処理可能にする。
        // canvas1 の内容は文字列 (Base64) にしてからポストする。(JSON として送信するため)
        const b64 = window.btoa(ba);
        let obj = {
            type:blob.type,
            size:blob.size,
            buffer:b64
        }
        // JSON として送信する。
        const data = JSON.stringify(obj);
        fetch('/fetch/postBlob', {method:"POST", headers:{'Content-Type':'application/json'}, body:data})
        .then(res => res.text())
        .then(msg => element('message').innerText = msg)  // 結果をメッセージとして受け取る。
        .catch(err => element('message').innerHTML = err.message);
    }, 'image/png');
});
```

##サーバ側 (fetch.js)

```js
router.post('/postBlob', (req, res) => {
    let fileName = path.dirname(__dirname).replace(/\\/g, "/") + "/tmp/canvas1.png";  // BLOB の保存先を決める。
    // atob() は "npm i atob" でインストールが必要。
    let buffer = atob(req.body.buffer).split(',');  // req.body.buffer はカンマ区切り文字列なので、カンマで区切って配列にする。
    let data = new Uint8Array(req.body.size);  // バイナリーデータ用のバッファを用意する。
    buffer.forEach((v, i) => data[i] = parseInt(v));  // buffer 内の文字列を整数に変換する。
    fs.writeFile(fileName, data, {encoding:null}, (err) => {  // バイナリーに変換されたデータ (data) をファイルに書く。
        if (err) {
            res.send("エラー：" + err.message);
        }
        else {
            res.send(`canvas1 を ${fileName} に保存しました。`);  // クライアントへメッセージを返す。
        }
    });
});
```

終わり