5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

GET Text

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

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

クライアント側

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 の値を取得しています。

// 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 形式でデータを受け取る例です。

クライアント側

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)

// 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 とは意味が異なる)

クライアント側

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)

// 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 として受け取り文字列として表示します。

クライアント側

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)

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

POST JSON

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

クライアント側

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)

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 データベースオブジェクトを取得する関数で次のように定義しています。

// 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" というミドルウェアを使用しています。このミドルウェアは "multipart/form-data" に対応しているので、ファイルアップロード (input[type="file") も可能です。

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

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

クライアント側

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 オブジェクトをサーバへ送り結果を受け取る例です。

クライアント側

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 としてサーバへ送ります。

クライアント側

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)

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} に保存しました。`);  // クライアントへメッセージを返す。
        }
    });
});

終わり

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?