kintone: Upload File

kintone: ファイルアップロード

This item uploads and adds files to an Attachment field of a record in a Kintone App.

Auto Step icon
Basic Configs
Step Name
Note
Configs for this Auto Step
conf_auth
C1: Authorization Setting in which API Token is set *
conf_basic
C2: Basic Auth Setting (required if enabled on Kintone)
conf_domain
C3: Domain (such as xxxxx.kintone.com or xxxxx.cybozu.com) *
conf_guestSpaceId
C4: Guest Space ID (required if the App is in a Guest Space)
conf_appId
C5: App ID *
conf_recordId
C6: Record ID *
conf_fieldCode
C7: Field Code of Attachment field *
conf_files
C8: File type data item whose attached files will be uploaded *

Notes

  • To get an API Token, open the Settings of the App and click “API Token” in the App Settings tab on Kintone

    Click “Generate”, select Permissions (“Edit records” permission is required), and click “Save”

    Do not forget to click “Update App” to apply the update
  • Guest Space ID (only when the Kintone App is in a guest space) and App ID can be confirmed in the API Token settings on Kintone

Capture

See Also

Script (click to open)
  • An XML file that contains the code below is available to download
    • kintone-file-upload.xml (C) Questetra, Inc. (MIT License)
    • If you are using Professional, you can modify the contents of this file and use it as your own add-on auto step


main();
function main(){
  //// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
  const auth = configs.getObject("conf_auth");
  const basic = configs.getObject("conf_basic");
  const domain = configs.get("conf_domain");
  const guestSpaceId = configs.get("conf_guestSpaceId");
  const appId = configs.get("conf_appId");
  const recordId = retrieveRecordId();
  const fieldCode = configs.get("conf_fieldCode");
  const filesDef = configs.getObject("conf_files");

  const apiToken = auth.getToken();
  let files = engine.findData( filesDef );
  if (files === null) { // 追加するファイルがない場合は何もせず正常終了
    return;
  }

  //// == 演算 / Calculating ==
  checkDomainAndIds( domain, appId, recordId );
  const apiUri = determineApiUri( domain, guestSpaceId ); // /v1/ までの URI
  const attachments = getAttachmentsInfo( apiUri, apiToken, basic, appId, recordId, fieldCode );
  uploadAndAddToAttachments( apiUri, apiToken, basic, files, attachments );
  updateAttachmentField( apiUri, apiToken, basic, appId, recordId, fieldCode, attachments );
}

/**
  * config からレコード ID を読み出す
  * @return {Stromg} recordId  レコード ID
  */
function retrieveRecordId() {
  const recordIdDef = configs.getObject( "conf_recordId" );
  let recordId = configs.get( "conf_recordId" );
  if ( recordIdDef !== null ) {
    recordId = engine.findData( recordIdDef );
  }
  return recordId;
}

/**
  * ドメイン、アプリ ID、レコード ID が空または不正な文字列であればエラーとする
  * @param {String} domain  ドメイン
  * @param {String} appId  アプリ ID
  * @param {String} recordId  レコード ID
  */
function checkDomainAndIds( domain, appId, recordId ) {
  if ( domain === "" || domain === null ) {
    throw "Domain is empty.";
  }
  const reg = new RegExp( '^[0-9a-zA-Z-]{3,32}.(?:kintone.com|cybozu.com)$' );
  if ( !reg.test(domain) ) {
    throw "Invalid Kintone domain.";
  }
  if ( appId === "" || appId === null ) {
    throw "App ID is empty.";
  }
  if ( !isValidId(appId) ) {
    throw "Invalid App ID.";
  }
  if ( recordId === "" || recordId === null ) {
    throw "Record ID is empty.";
  }
  if ( !isValidId(recordId) ) {
    throw "Invalid Record ID.";
  }
}

/**
  * ID が有効か(自然数か)を判定する
  * @param {String} idString  ID の文字列
  * @return {Boolean}  有効な ID かどうか
  */
function isValidId( idString ) {
  const idReg = new RegExp( '^[1-9][0-9]*$' );
  return idReg.test( idString );
}

/**
  * kintone REST API の URI を決定する(/v1/ まで)
  * ドメインが空、または kintone のドメインとして不正な文字列であればエラーとする
  * @param {String} domain  ドメイン
  * @param {String} guestSpaceId  ゲストスペース ID
  * @return {String} apiUri  API の URI(/v1/ まで)
  */
function determineApiUri( domain, guestSpaceId ) {
  let apiUri = "";
  if ( guestSpaceId === "" || guestSpaceId === null ) {
    apiUri = `https://${domain}/k/v1/`;
  } else {
    if ( !isValidId(guestSpaceId) ) {
      throw "Invalid Guest Space ID.";
    }
    apiUri = `https://${domain}/k/guest/${guestSpaceId}/v1/`;
  }
  return apiUri;
}

/**
  * kintone REST API にレコード取得の GET リクエストを送信し、添付ファイルの情報の配列を返す
  * @param {String} apiUri  API の URI(/v1/ まで)
  * @param {String} apiToken  API トークン
  * @param {AuthSettingWrapper} basic  Basic 認証設定
  * @param {String} appId  アプリ ID
  * @param {String} recordId  レコード ID
  * @param {String} fieldCode  添付ファイルフィールドのフィールドコード
  * @return {Array<Object>} attachments  添付ファイル情報 {contentType, fileKey, name, size} の配列
  */
function getAttachmentsInfo( apiUri, apiToken, basic, appId, recordId, fieldCode ) {
  let request = httpClient.begin()
    .header( "X-Cybozu-API-Token", apiToken )
    .queryParam( "app", appId )
    .queryParam( "id", recordId );
  if (basic !== null) {
    request = request.authSetting(basic);
  }
  const response = request.get( `${apiUri}record.json` );
  //when error thrown
  const responseStr = response.getResponseAsString();
  const status = response.getStatusCode();
  if (status !== 200) {
    const accessLog = `---GET request--- ${status}\n${responseStr}\n`;
    engine.log(accessLog);
    throw `Failed to get record. status: ${status}`;
  }
  const json = JSON.parse(responseStr);
  if ( json.record[fieldCode] === undefined ) { // 一致するフィールドコードがない場合、エラー
    throw `${fieldCode} does not exist in the record.`;
  }
  if ( json.record[fieldCode].type !== "FILE" ) { // 添付ファイルフィールドでない場合、エラー
    throw `${fieldCode} is not an Attachment field.`;
  }
  return json.record[fieldCode].value;
}

/**
  * kintone REST API にファイルアップロードの POST リクエストを送信し、ファイル情報の配列を返す
  * @param {String} apiUri  API の URI(/v1/ まで)
  * @param {String} apiToken  API トークン
  * @param {AuthSettingWrapper} basic  Basic 認証設定
  * @param {Array<QfileView>} files  アップロードするファイルの配列
  * @param {Array<Object>} attachments  ファイル情報 {fileKey, etc.} の配列
  */
function uploadAndAddToAttachments( apiUri, apiToken, basic, files, attachments ) {
  const fileNum = files.length;
  if ( fileNum + 2 > httpClient.getRequestingLimit() ) { // HTTP リクエストの上限を超える場合はエラー
    throw "Necessary HTTP requests exceeds the limit.";
  }
  files.forEach( file => {
    const response = uploadFile( apiUri, apiToken, basic, file );
    const responseStr = response.getResponseAsString();
    const json = JSON.parse(responseStr); // {fileKey}
    attachments.push( json );
  });
}

/**
  * kintone REST API にファイルアップロードの POST リクエストを送信し、レスポンスを返す
  * @param {String} apiUri  API の URI(/v1/ まで)
  * @param {String} apiToken  API トークン
  * @param {AuthSettingWrapper} basic  Basic 認証設定
  * @param {QfileView} file  アップロードするファイル
  * @return {HttpResponseWrapper} response  レスポンス
  */
function uploadFile( apiUri, apiToken, basic, file ) {
  let request = httpClient.begin()
    .header( "X-Cybozu-API-Token", apiToken )
    .multipart( "file", file );
  if (basic !== null) {
    request = request.authSetting(basic);
  }
  const response = request.post( `${apiUri}file.json` );
  //when error thrown
  const status = response.getStatusCode();
  if (status !== 200) {
    engine.log(`File Name: ${file.getName()}`);
    engine.log(`Content Type: ${file.getContentType()}`);
    const responseStr = response.getResponseAsString();
    const accessLog = `---POST request--- ${status}\n${responseStr}\n`;
    engine.log(accessLog);
    throw `Failed to upload file. status: ${status}`;
  }
  return response;
}

/**
  * kintone REST API にレコード更新の PUT リクエストを送信し、添付ファイルフィールドを更新する
  * @param {String} apiUri  API の URI(/v1/ まで)
  * @param {String} apiToken  API トークン
  * @param {AuthSettingWrapper} basic  Basic 認証設定
  * @param {String} appId  アプリ ID
  * @param {String} recordId  レコード ID
  * @param {String} fieldCode  添付ファイルフィールドのフィールドコード
  * @param {Array<Object>} attachments  添付ファイル情報 {fileKey} の配列
  */
function updateAttachmentField( apiUri, apiToken, basic, appId, recordId, fieldCode, attachments ) {
  const body = {
    "app": appId,
    "id": recordId,
    "record": {}
  };
  body.record[fieldCode] = {
    "value": attachments
  };
  let request = httpClient.begin()
    .header( "X-Cybozu-API-Token", apiToken )
    .body(JSON.stringify(body), "application/json; charset=UTF-8");
  if (basic !== null) {
    request = request.authSetting(basic);
  }
  const response = request.put( `${apiUri}record.json` );
  //when error thrown
  const status = response.getStatusCode();
  if (status !== 200) {
    engine.log(`Request Body: ${JSON.stringify(body)}`);
    const responseStr = response.getResponseAsString();
    const accessLog = `---PUT request--- ${status}\n${responseStr}\n`;
    engine.log(accessLog);
    throw `Failed to update record. status: ${status}`;
  }
}

  
%d