ファイルのアップロード・ダウンロード

にメンテナンス済み

kintone の添付ファイルフィールドに対して、REST API でファイルをアップロード・ダウンロードする操作は、多くのカスタマイズで必要になります。しかし、ファイル操作には通常のレコード CRUD とは異なる手順が必要で、ファイルキーという概念を理解する必要があります。

この記事では、kintone REST API でのファイル操作を、kintone 内の JavaScript カスタマイズと Node.js 環境の両方から解説します。

ファイル操作の基本的な流れ

kintone のファイル操作は、以下の 2 段階で行います。

アップロードの流れ

  1. ファイルアップロード API でファイルをアップロードし、fileKey(ファイルキー)を取得する
  2. レコードの追加・更新 API で、添付ファイルフィールドに fileKey を指定して保存する

ダウンロードの流れ

  1. レコード取得 API で添付ファイルフィールドの情報(fileKey を含む)を取得する
  2. ファイルダウンロード APIfileKey を指定してファイルの実データを取得する
チェック

ファイルキーは一時的な識別子です。アップロード API で取得したファイルキーは、同一リクエストセッション内でのみ有効で、3 日間使用されないと失効します。

ファイルのアップロード

kintone 内から利用する場合

kintone の JavaScript カスタマイズでは、XMLHttpRequest を使ってファイルをアップロードします。

file-upload.js
/**
 * kintone にファイルをアップロードし、ファイルキーを取得する
 * @param { File | Blob } file - アップロードするファイル
 * @param { string } [fileName] - ファイル名(Blobの場合は指定必須)
 * @returns { Promise<string> } ファイルキー
 */
const uploadFile = (file, fileName) => {
  return new Promise((resolve, reject) => {
    const formData = new FormData();
    formData.append('file', file, fileName || file.name);

    const xhr = new XMLHttpRequest();
    xhr.open('POST', kintone.api.url('/k/v1/file.json', true));
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    xhr.onload = () => {
      if (xhr.status === 200) {
        const response = JSON.parse(xhr.responseText);
        resolve(response.fileKey);
      } else {
        reject(new Error(`ファイルアップロードに失敗しました: ${xhr.status}`));
      }
    };

    xhr.onerror = () => reject(new Error('ネットワークエラーが発生しました'));

    xhr.send(formData);
  });
};

ファイルをアップロードしてレコードに添付する

取得したファイルキーを使って、レコードの添付ファイルフィールドに設定します。

upload-and-attach.js
(() => {
  'use strict';

  /**
   * ファイルをアップロードし、新規レコードの添付ファイルフィールドに設定して保存する
   * @param { File } file - アップロードするファイル
   * @param { string | number } app - アプリID
   */
  const uploadAndCreateRecord = async (file) => {
    // Step 1: ファイルをアップロードしてファイルキーを取得
    const fileKey = await uploadFile(file);

    // Step 2: ファイルキーを指定してレコードを追加
    const body = {
      app: kintone.app.getId(),
      record: {
        添付ファイル: {
          value: [{ fileKey }],
        },
        タイトル: {
          value: file.name,
        },
      },
    };

    const response = await kintone.api(
      kintone.api.url('/k/v1/record.json', true),
      'POST',
      body
    );

    console.log('レコードが作成されました:', response);
    return response;
  };

  kintone.events.on('app.record.index.show', (event) => {
    const headerSpace = kintone.app.getHeaderMenuSpaceElement();

    // ファイル選択用のinput要素を作成
    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.onchange = async (e) => {
      const file = e.target.files[0];
      if (!file) return;

      try {
        await uploadAndCreateRecord(file);
        alert('ファイルをアップロードし、レコードを作成しました');
        location.reload();
      } catch (error) {
        alert('アップロードに失敗しました');
        console.error(error);
      }
    };

    const button = document.createElement('button');
    button.textContent = 'ファイルから登録';
    button.onclick = () => fileInput.click();

    headerSpace.append(button);

    return event;
  });
})();

既存レコードに添付ファイルを追加する

既にファイルが添付されているレコードにファイルを追加する場合は、既存のファイル情報を取得してから追加する必要があります。

append-file.js
/**
 * 既存レコードにファイルを追加する
 * @param { string | number } app - アプリID
 * @param { string | number } id - レコードID
 * @param { string } fieldCode - 添付ファイルフィールドのフィールドコード
 * @param { File } file - 追加するファイル
 */
const appendFileToRecord = async (app, id, fieldCode, file) => {
  // Step 1: 既存のレコードから現在の添付ファイル情報を取得
  const getResponse = await kintone.api(
    kintone.api.url('/k/v1/record.json', true),
    'GET',
    { app, id }
  );

  const existingFiles = getResponse.record[fieldCode].value;

  // Step 2: 新しいファイルをアップロード
  const fileKey = await uploadFile(file);

  // Step 3: 既存のファイルキー + 新しいファイルキーでレコードを更新
  const updatedFiles = [
    ...existingFiles.map((f) => ({ fileKey: f.fileKey })),
    { fileKey },
  ];

  await kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', {
    app,
    id,
    record: {
      [fieldCode]: { value: updatedFiles },
    },
  });
};
チェック

添付ファイルフィールドを更新する際、既存のファイルキーを含めずに更新すると、既存のファイルが削除されます。ファイルを追加する場合は、必ず既存のファイル情報もあわせて指定してください。

ファイルのダウンロード

kintone 内から利用する場合

kintone の JavaScript カスタマイズでは、XMLHttpRequest を使ってファイルをダウンロードします。

file-download.js
/**
 * kintone からファイルをダウンロードする
 * @param { string } fileKey - ファイルキー
 * @returns { Promise<Blob> } ダウンロードしたファイルデータ
 */
const downloadFile = (fileKey) => {
  return new Promise((resolve, reject) => {
    const url = kintone.api.url('/k/v1/file.json', true) + `?fileKey=${fileKey}`;

    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.responseType = 'blob';

    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response);
      } else {
        reject(new Error(`ファイルダウンロードに失敗しました: ${xhr.status}`));
      }
    };

    xhr.onerror = () => reject(new Error('ネットワークエラーが発生しました'));

    xhr.send();
  });
};

ダウンロードしたファイルをブラウザで保存する

ダウンロードした Blob データをブラウザのダウンロードダイアログで保存する方法です。

save-file.js
/**
 * Blob データをファイルとしてダウンロードする
 * @param { Blob } blob - ダウンロードするデータ
 * @param { string } fileName - 保存するファイル名
 */
const saveBlobAsFile = (blob, fileName) => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = fileName;
  document.body.append(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
};

レコードの添付ファイルをダウンロードする完全な例

download-attachment.js
(() => {
  'use strict';

  kintone.events.on('app.record.detail.show', (event) => {
    const record = event.record;
    const files = record['添付ファイル'].value;

    if (files.length === 0) return event;

    const headerSpace = kintone.app.record.getHeaderMenuSpaceElement();

    // 各ファイルのダウンロードボタンを設置
    files.forEach((file) => {
      const button = document.createElement('button');
      button.textContent = `${file.name} をダウンロード`;
      button.style.marginRight = '8px';

      button.onclick = async () => {
        try {
          const blob = await downloadFile(file.fileKey);
          saveBlobAsFile(blob, file.name);
        } catch (error) {
          alert('ダウンロードに失敗しました');
          console.error(error);
        }
      };

      headerSpace.append(button);
    });

    return event;
  });
})();

Node.js 環境からの操作

@kintone/rest-api-client を使った場合

Node.js 環境では @kintone/rest-api-client を使うと簡潔に記述できます。

nodejs-file-upload.js
const { KintoneRestAPIClient } = require('@kintone/rest-api-client');
const fs = require('fs');
const path = require('path');

const client = new KintoneRestAPIClient({
  baseUrl: 'https://your-domain.cybozu.com',
  auth: { apiToken: 'your-api-token' },
});

// ファイルのアップロード
const uploadAndAttach = async (filePath, app) => {
  const fileName = path.basename(filePath);
  const fileData = fs.readFileSync(filePath);

  // Step 1: ファイルアップロード
  const { fileKey } = await client.file.uploadFile({
    file: { name: fileName, data: fileData },
  });

  // Step 2: レコードに添付
  const response = await client.record.addRecord({
    app,
    record: {
      添付ファイル: { value: [{ fileKey }] },
      タイトル: { value: fileName },
    },
  });

  console.log('レコード作成完了:', response);
};

// ファイルのダウンロード
const downloadAttachment = async (app, recordId, fieldCode, outputDir) => {
  // レコードを取得してファイル情報を取得
  const { record } = await client.record.getRecord({ app, id: recordId });
  const files = record[fieldCode].value;

  for (const file of files) {
    const fileData = await client.file.downloadFile({ fileKey: file.fileKey });

    const outputPath = path.join(outputDir, file.name);
    fs.writeFileSync(outputPath, Buffer.from(fileData));
    console.log(`ダウンロード完了: ${outputPath}`);
  }
};
Node.js環境でのREST API Client
REST API Client を活用するうえで、私が注意しているポイントを紹介します。

添付ファイルフィールドのデータ構造

参考として、添付ファイルフィールドのレコードデータ構造を示します。

添付ファイルフィールドのデータ構造
{
  "添付ファイル": {
    "type": "FILE",
    "value": [
      {
        "contentType": "application/pdf",
        "fileKey": "20260224abcdef1234",
        "name": "報告書.pdf",
        "size": "1048576"
      },
      {
        "contentType": "image/png",
        "fileKey": "20260224ghijkl5678",
        "name": "スクリーンショット.png",
        "size": "204800"
      }
    ]
  }
}
チェック

value 配列内の各オブジェクトには、contentType(MIME タイプ)、fileKeyname(ファイル名)、size(バイト数の文字列)が含まれます。

まとめ

  • kintone のファイル操作は「ファイルアップロード → ファイルキー取得 → レコード保存」の 2 段階で行う
  • アップロードには XMLHttpRequestFormData を使用する
  • 既存レコードにファイルを追加する場合は、既存のファイルキーもあわせて指定する必要がある
  • ダウンロードは responseType: 'blob' で取得し、URL.createObjectURL でブラウザに保存できる
  • Node.js 環境では @kintone/rest-api-client を使うと簡潔に記述できる
#kintone #JavaScript #TypeScript