ExpressでDBデータをエクスポート&インポート

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、前回はNode.jsExpress)の「なくてもいいけど、あれば嬉しいよね」機能として、「Node.jsでTwitterに投稿する」という記事をお届けしました。

そして、今回はその第2弾として、

DBのエクスポート&インポート機能 

をご紹介したいと思います。

特にエクスポートはクライアントさんによっては必須機能と考えている方もいらっしゃるぐらい、あれば結構重宝する機能ですし、、インポートはサイト開設時に別管理していたデータを移行するのに便利なので、こちらも同様にもつけて開発をしてみました。(バリデーション機能つきです✨)

ぜひ皆さんのお役に立てると嬉しいです😊✨

開発完了: Node.js 8、Express 4.1

前提として

今回ご紹介する内容は、データベースの操作にSequelizeを使っています。もしSequlizeを使いたい場合は以下2つの記事を参考にしてみてください。

なお、Sequelizeを使わなくても今回の記事を参考にすれば簡単にDBエクスポート&インポートができると思うので、ぜひ参考にしてみてくださいね。

必要なパッケージをインストールする

Node.jsExcelCSVを操作するパッケージexceljsを以下のコマンドでインストールしてください。

npm i --save exceljs
npm i --save express-fileupload
npm i --save validator

express-fileuploadexpress-validatorは、インポート時のファイル送信とバリデーションで使います。

エクスポートする

CSVでエクスポートする

const ExcelJS = require('exceljs');

app.get('/export', (req, res) => {

  // DBデータを取得
  const users = User.findAll()
    .then(users => {

      const workbook = new ExcelJS.Workbook();
      const worksheet = workbook.addWorksheet('sheet-name');

      // 1行ずつエクスポートするデータをセット
      users.forEach((user, index) => {

        let rowIndex = index + 1;
        worksheet.getRow(rowIndex).values = [
          user.id,
          user.name,
          user.email
        ];

      });

      // CSVでエクスポート
      workbook.csv.writeBuffer()
        .then(buffer => {

          const fileName = 'users.csv';
          res.setHeader('Content-Type', 'text/csv; charset=UTF-8');
          res.setHeader('Content-disposition', 'attachment; filename='+ fileName);
          res.send(buffer);

        });

    });

});

Excelファイル(xlsx)でエクスポートする

CSVでエクスポートする」とほぼ同じなので、workbook.xlsx.writeBuffer()の部分だけを以下のように変更してください。

// Excelファイルでエクスポート
workbook.xlsx.writeBuffer()
  .then(buffer => {

    const fileName = 'users.xlsx';
    res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    res.setHeader('Content-disposition', 'attachment; filename='+ fileName);
    res.send(buffer);

  });

インポートする

パッケージを読み込む

まず、ファイルのアップロードとバリデーションのために、先ほどインストールしたパッケージを読み込んでおきましょう。

const ExcelJS = require('exceljs');
const fileUpload = require('express-fileupload');
const validator = require('validator');

ミドルウェアを設定する

続いて、ファイル・アップロードのためにミドルウェアを設定します。

app.use(fileUpload({ useTempFiles: true }));

useTempFilesは、送信されたファイルを一時ファイルとして利用できるようにするオプションです。インポートファイルの読み込みが楽になるので、今回はこのオプションを利用します。

※もしファイルだけでなく、通常の入力も取得したい場合は以下も同様に追加しておいてください。

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

ファイルをアップロードする部分をつくる

では、ファイルをアップロードするためのフォームを作ります。
まずはルートです。

app.get('/import', (req, res) => {

  res.render('import');

});

そして、ビューです。
パスは、「views/import.mst」です。

<html>
<body>
<div id="app">
    <input type="file" accept=".csv, .xlsx" @change="onFileChange">
    <br>
    <button type="button" @click="onSubmit">送信する</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>

    new Vue({
        el: '#app',
        data: {
            importFile: null
        },
        methods: {
            onFileChange(e) {

                const files = e.target.files;

                if(files.length > 0) {

                    this.importFile = files[0];

                }

            },
            onSubmit() {

                let formData = new FormData();
                formData.append('importFile', this.importFile);
                axios.post('/import', formData)
                    .then(response => {

                        if(response.data.result) {

                            alert('インポート完了!');

                        }

                    });

            }
        }
    });

</script>
</body>
</html>

実際にブラウザで確認すると以下のようになります。

ファイルを受信する部分をつくる

では、実際にファイルを受け取ってデータベースに追加する部分です。
CSV、エクセルファイルどちらにも対応しています。

app.post('/import', (req, res) => {

    try {

        const workbook = new ExcelJS.Workbook();
        const importFile = req.files.importFile;
        const tempFilePath = importFile.tempFilePath;
        const mimeType = importFile.mimetype;
        let reader;

        if(mimeType === 'text/csv') {

            reader = workbook.csv.readFile(tempFilePath);

        } else if(mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {

            reader = workbook.xlsx.readFile(tempFilePath);

        }

        reader.then(() => { // インポート

            const worksheet = workbook.getWorksheet(1);
            let data = [];

            for(let i = 1; i <= worksheet.rowCount; i++) {

                const row = worksheet.getRow(i);
                const name = row.getCell(1).value;
                const email = row.getCell(2).value;
                const password = row.getCell(3).value;

                if(validator.isEmpty(name) || validator.isEmpty(password) || !validator.isEmail(email)) {

                    throw new Error('データが不完全なため処理できませんでした。');

                }

                data.push({
                    name: name,
                    email: email,
                    password: password,
                    createdAt: new Date(),
                    updatedAt: new Date()
                });

            }

            User.bulkCreate(data);
            res.json({ result: true });

        })
        .catch(error => {

            return res.status(400).send(error.message);

        });

    } catch(error) {

        return res.status(400).send(error.message);

    }

});

コードの流れとしては、以下のとおりです。

  1. 送信されたファイル(一時ファイル)を読み込む
  2. 読み込まれたデータを1行ずつバリデーションする
  3. バリデーションを全て通過したらデータをDBに追加する

テストしてみる

では、実際にブラウザからインポートを実行してテストしてみましょう。
事前情報として、ユーザーテーブルはこのようになっています。

そして、ここに六郎さんデータが入ったエクセルファイルをインポートしてみます。

では、ファイルを選択して送信します。

すると、以下のポップアップが表示されました。

DBの方をチェックしてみましょう。

六郎さんが追加されました。
成功です😊✨

ちなみに、メールアドレスをわざと間違えてファイル送信した場合は以下のようになりました。うまくバリデーションも機能しています!

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回はExpressを使ったDBのエクスポート/インポート方法をご紹介しました。

ちなみに今回の開発で感じたことは、「えっ、送信されたファイルを取得するにはパッケージが必要なの!?」ということでした。

Expressは必要最低限の機能に絞っているので、Laravelでは当たり前の機能がついていなかったりします。(なので、「手のかかる子供ほどかわいい」状態にもなりやすいのですが😉)

とはいえ、レスポンス速度は超高速なので、開発するサイトによって選択をするというスタンスがいいかと思います。

ぜひ皆さんもやってみてくださいね。

ではでは〜!

このエントリーをはてなブックマークに追加       follow us in feedly