新規投稿
フォローする

CSV指定でファイル一括アップロード

CSV指定で添付ファイルを一括アップロードするコードをご紹介します.

動作環境

Node.js v12以上で動作します.

用意するファイル

sample.csv

先頭行はフィールド名,文字コードはShift_JIS,区切り文字はカンマ「,」,囲み文字はダブルクォート「"」としてください. 添付ファイルは,main.jsからの相対パスで指定してください. 添付ファイルを複数指定する場合は,カンマ「,」で区切ってください.

・例

"レコード番号","文字列 (1行)","数値","ラジオボタン","添付ファイル"
"2","abc","1","sample1","sample_image1.jpg"
"1","a","1","sample1","sample_image1.jpg,sample_image2.jpg"

※チェックボックス,複数選択,ユーザー選択,組織選択,グループ選択,サブテーブルには非対応です. kintoneのファイル書き出し機能で出力したCSVファイルを利用する場合は,上記フィールドを除外してください.

main.js

9~18行目は適宜変更してください.

(async () => {
  const util = require('util');
  const fs = require('fs');
  const iconv = require('iconv-lite');
  const {KintoneRestAPIClient} = require('@kintone/rest-api-client');
  const csvParse = require('csv-parse/lib/sync');
  const readFile = util.promisify(fs.readFile);

  const client = new KintoneRestAPIClient({
    baseUrl: 'https://****.cybozu.com', //kintoneのURL
    auth: {
      username: '****', //kintoneのログイン名
      password: '****', //kintoneのパスワード
    },
  });
  const app = ****; //アプリID
  const fileName = 'sample.csv'; //CSVファイルのパス
  const reverse = true; //データをCSVの逆順に登録するか否か

  const formFields = await client.app.getFormFields({app});
  const datas = csvParse(
    iconv.decode(
      Buffer.from(await readFile(fileName),
      'binary'
    ),
    'Shift_JIS'
  ), {
    delimiter: ',',
    rowDelimiter: 'auto',
    quote: '"'
  });
  const headers = datas[0].reduce((headers, header, index) => {
    const property = Object.values(formFields.properties).find(property => property.label === header);
    if([
      'RECORD_NUMBER',
      '__ID__',
      '__REVISION__',
      'CREATOR',
      'MODIFIER',
      'CREATED_TIME',
      'UPDATED_TIME',
      'CATEGORY',
      'STATUS',
      'STATUS_ASSIGNEE',
    ].includes(property.type)) return headers;
    headers.push({
      index,
      type: property.type,
      code: property.code
    });
    return headers;
  }, []);
  const files = await Promise.all(
    datas.slice(1).map(
      data => headers.filter(header => header.type === 'FILE').map(
        header => data[header.index].split(',')
      )
    ).flat(2).map(path => client.file.uploadFile({
      file: {
        path
      }
    }))
  );
  let fileIndex = 0;
  const records = datas.slice(1).map(data => headers.reduce((record, header) => {
    if(header.type === 'FILE') {
      record[header.code] = {
        value: data[header.index].split(',').map(filePath => {
          return files[fileIndex++]
        })
      };
    } else {
      record[header.code] = {value: data[header.index]};
    }
    return record;
  }, {}));
  if(reverse) records.reverse();
  await client.record.addAllRecords({app, records});
  console.log('レコードを追加しました.');
})();

必要パッケージのインストール

$ npm i iconv-lite csv-parse @kintone/rest-api-client

実行

$ node main.js
0

7件のコメント

Avatar
kintone移行検討中

node.jsも初心者ですが、インストールを終え、node main.js実行時、

(node:5744) UnhandledPromiseRejectionWarning: Error: Cannot find module 'iconv-lite'
Require stack:
- D:\WK\main.js
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
at Function.Module._load (internal/modules/cjs/loader.js:725:27)
at Module.require (internal/modules/cjs/loader.js:952:19)
at require (internal/modules/cjs/helpers.js:88:18)
at D:\WK\main.js:4:17
at Object.<anonymous> (D:\WK\main.js:80:3)
at Module._compile (internal/modules/cjs/loader.js:1063:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:5744) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:5744) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

となります。

0
Avatar
kintone移行検討中

npm install -g iconv-lite

は実行しましたが、同じです。

0
Avatar
江田篤史

kintone移行検討中さん

-gオプションを外して,iconv-liteをローカルインストールしても動きませんか?

npm i iconv-lite

もしグローバルにインストールするのであれば,環境変数にグローバルのnode_modulesのパスを追加する必要があります.
https://qiita.com/takiru/items/391817ea6c62837ace5a

0
Avatar
kintone移行検討中

iconv-lite

@kintone/rest-api-client

csv-parse

をインストールして無事動作しました。ありがとうございます。

ユーザー名を移行するにあたって、考え方として1行テキストで移行とユーザー選択を考えていますが、ユーザー選択に移行するには、どのような手続きが必要でしょうか?表示名 or ログイン名、複数値など考え方について教えてください。

0
Avatar
kintone移行検討中

よく調べもしないで、すいませんでした。

ログイン名で改行をつけるイメージですね。

これで、いろいろ試してみます。

ありがとうございました。

0
Avatar
kintone移行検討中

やはり一癖ありそうです。

UIからの操作ではデータ形式も確認できたのですが、JSからはエラーになります。

エクスポートデータでは複数値は改行でしたが、スクリプト例ではカンマだったと思います。

どちらでもよいのでしょうか。

UIからはフィールドの位置とレコード番号のキーの解除が必要ですが、スクリプトでは特に意識しなくてもよいのでしょうか。

現在の課題として

・ユーザー選択

・作成者

・作成日時

・更新者

・更新日時

の移行について検証中です。

調べていると、このnode.jsの話題とJavascriptによる話題が全く別次元に見えるのですが、バッチ操作と画面からの操作と考えればよいのでしょうか?100件ごとのループなど制限は同じでしょうか?

0
Avatar
江田篤史

kintone移行検討中さん

kintoneのデフォルトの「ファイルから読み込む」という機能とは別物になります.

本記事の機能は,kintone REST APIを利用して作成しています.
記事に注釈として書いておりますが,記事内のコードでは「チェックボックス,複数選択,ユーザー選択,組織選択,グループ選択,サブテーブル」には非対応です.

「ユーザー選択」に対応させる場合は,登録するレコードがこちらのユーザー選択の形式になるようにCSVを整形するコードを追加することになります.

main.jsの65行目で登録するレコードを定義しているので,そちらを変更します.

const records = datas.slice(1).map(data => headers.reduce((record, header) => {
  if(header.type === 'FILE') {
    record[header.code] = {
      value: data[header.index].split(',').map(filePath => {
        return files[fileIndex++]
      })
    };
  } else if(header.type === 'USER_SELECT') {
    record[header.code] = {
      value: data[header.index].split(',').map(code => {
        return {code}
      })
    };
  } else {
    record[header.code] = {value: data[header.index]};
  }
  return record;
}, {}));

CSVは,ユーザー選択を複数入力する場合はカンマ区切りで入力してください.

"レコード番号","文字列 (1行)","数値","ラジオボタン","添付ファイル","ユーザー選択"
"2","abc","1","sample1","sample_image1.jpg","user1,user2"
"1","a","1","sample1","sample_image1.jpg,sample_image2.jpg","user3"

「作成者,作成日時,更新者,更新日時」については,自動的に入力される値であり,こちらから任意に入力することはできません.
これは,kintoneのデフォルトの「ファイルから読み込む」という機能においても同じかと思います.

レコード数については,無制限です.
@kintone/rest-api-clientのaddAllRecordsメソッドがうまく処理してくれています.

0
サインインしてコメントを残してください。