Box: Download File as PDF/Text/Image
This item downloads the specified file on Box in the specified file format.
Configs: Common
  • Step Name
  • Note
Configs
  • C1: OAuth2 Setting *
  • C2: File ID to download *
  • C3: File Format in which to download the file *
  • C4: File Name (named with the file name on Box if blank)#{EL}
  • C5: File type data item to add the downloaded file *

Notes

  • The refresh token for Box has an expiration date. Use regularly to ensure that it does not exceed the expiration.
  • This item uses Box file representation API. For supported file types, see the Box Developer Documentation.
  • For some file formats like JPEG (2048×2048), PNG (1024×1024) and PNG (2048×2048), each page of the original file will be downloaded as a separate file. The downloaded files will have a page number attached at the beginning of their file names.
  • A script error saying “The representation [xxx] is being created and not available yet. Please try again later.” may occur when downloading a file in the file format for the first time. In such case, re-execute this item some time later to successfully download the file.

Capture

See also

Script (click to open)
  • An XML file that contains the code below is available to download


main();
function main(){
    const oauth2 = configs.get('conf_OAuth2');
    const fileId = decideFileId();
    const representation = configs.get('conf_Representation');
    const fileName = configs.get('conf_FileName');
    const fileDef = configs.getObject("conf_Files");

    const {urlTemplate, paged} = getRepresentationInfo(oauth2, fileId, representation);
    if (paged !== 'true') { // ページ割されていない場合
        const qfile = download(oauth2, urlTemplate, representation, fileName);
        saveFile(fileDef, qfile);
        return;
    }
    // ページ割されている場合
    const qfiles = downloadPages(oauth2, urlTemplate, representation, fileName);
    saveFiles(fileDef, qfiles);
}

/**
  * ファイル ID を config から読み出す
  * @return {String} fileId ファイル ID
  */
function decideFileId() {
    const fileId = engine.findData(configs.getObject('conf_FileId'));
    if (fileId === '' || fileId === null) {
        throw 'File ID is blank.';
    }
    return fileId;
}

/**
  * レプリゼンテーションの情報を取得する GET リクエストを送信
  * @param {String} oauth2 OAuth2 設定
  * @param {String} fileId ファイル ID
  * @param {String} representation レプリゼンテーション
  * @return {Object} representationInfo
  * @return {String} representationInfo.urlTemplate ダウンロードの際の URL テンプレート
  * @return {String|undefined} representationInfo.paged ページ割されているかどうか
  */
function getRepresentationInfo(oauth2, fileId, representation) {
    const url = `https://api.box.com/2.0/files/${encodeURIComponent(fileId)}`;
    const response = httpClient.begin()
        .authSetting(oauth2)
        .queryParam('fields', 'representations')
        .header('X-REP-HINTS', representation)
        .get(url);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to get representations. status:${status}`;
    }
    const {entries} = JSON.parse(responseStr).representations;
    if (entries.length === 0) {
        throw `The ${representation} representation is unavailable for this file.`;
    }
    const urlTemplate = entries[0].content.url_template;
    const paged = entries[0].properties.paged; // "true", "false" or undefined
    return {urlTemplate, paged};
}

/**
  * レプリゼンテーションをダウンロードする GET リクエストを送信
  * ページ割されていない場合
  * @param {String} oauth2 OAuth2 設定
  * @param {String} urlTemplate URL テンプレート
  * @param {String} representation レプリゼンテーション
  * @param {String} fileName ファイル名
  * @return {NewQfile} qfile ダウンロードしたファイル
  */
function download(oauth2, urlTemplate, representation, fileName) {
    const url = urlTemplate.replace('{+asset_path}', '');
    const response = httpClient.begin()
        .authSetting(oauth2)
        .queryParam('set_content_disposition_type', 'attachment')
        .get(url);
    const status = response.getStatusCode();
    if (status === 202) { // レプリゼンテーション作成中でダウンロードできない
        throw `The representation ${representation} is being created and not available yet. Please try again later.`;
    }
    if (status !== 200) { // その他のエラー
        engine.log(response.getResponseAsString());
        throw `Failed to download the representation ${representation}. status:${status}`;
    }
    const contentType = response.getContentType();
    const content = response.getResponse();
    const fileNameToSet = decideFileNameToSet(fileName, response);
    const qfile = new com.questetra.bpms.core.event.scripttask.NewQfile(
        fileNameToSet, contentType, content
    );
    return qfile;
}

/**
  * レプリゼンテーションをダウンロードする GET リクエストを送信
  * ページ割されている場合
  * @param {String} oauth2 OAuth2 設定
  * @param {String} urlTemplate URL テンプレート
  * @param {String} representation レプリゼンテーション
  * @param {String} fileName ファイル名
  * @return {Array<NewQfile>} qfiles ダウンロードしたファイルを格納した配列
  */
function downloadPages(oauth2, urlTemplate, representation, fileName) {
    const extension = representation.substring(1,4); // jpg or png
    const qfiles = [];
    let pageNum = 1;
    const httpLimit = httpClient.getRequestingLimit();
    while (true) {
        if (pageNum + 1 > httpLimit) { // レプリゼンテーション情報の取得で 1 回リクエスト済み
            throw 'Number of necessary HTTP requests exceeds the limit.';
        }
        const url = urlTemplate.replace('{+asset_path}', `${pageNum}.${extension}`);
        const response = httpClient.begin()
            .authSetting(oauth2)
            .queryParam('set_content_disposition_type', 'attachment')
            .get(url);
        const status = response.getStatusCode();
        if (pageNum === 1 && status === 202) { // レプリゼンテーション作成中でダウンロードできない
            throw `The representation ${representation} is being created and not available yet. Please try again later.`;
        }
        if (pageNum > 1 && status === 404) { // ページを最後までダウンロードした。ループから抜ける
            break;
        }
        if (status !== 200) { // その他のエラー
            engine.log(response.getResponseAsString());
            throw `Failed to download the page ${pageNum} of the representation ${representation}. status:${status}`;
        }
        const contentType = response.getContentType();
        const content = response.getResponse();
        let fileNameToSet = decideFileNameToSet(fileName, response);
        fileNameToSet = `${pageNum}-${fileNameToSet}`; // ファイル名にページ番号を追加
        const qfile = new com.questetra.bpms.core.event.scripttask.NewQfile(
            fileNameToSet, contentType, content
        );
        qfiles.push(qfile);
        pageNum++;
    }
    return qfiles;
}

/**
  * 保存時のファイル名を決定する
  * @param {String} fileName 新しく名前をつける場合のファイル名
  * @param {HttpResponseWrapper} response ファイルダウンロードのレスポンス
  * @return {String} fileNameToSet 設定するファイル名
  */
function decideFileNameToSet(fileName, response) {
    if (fileName !== '' && fileName !== null) {
        return fileName;
    }
    // ファイル名をレスポンスの Content-Disposition ヘッダから取得する
    const contentDisposition = response.getHeaderValues('Content-Disposition')[0];
    const matched = contentDisposition.match(/filename\*=UTF-8''(.+)/);
    const fileNameToSet = decodeURI(matched[1]);
    return fileNameToSet;
}

/**
  * 1 つのファイルを追加保存する
  * @param {ProcessDataDefinitionView} fileDef 保存先データ項目
  * @param {NewQfile} qfile 追加保存するファイル
  */
function saveFile(fileDef, qfile) {
    let files = engine.findData(fileDef);
    if (files === null) {
        files = new java.util.ArrayList();
    }
    files.add(qfile);
    engine.setData(fileDef, files);
}

/**
  * 複数のファイルを追加保存する
  * @param {ProcessDataDefinitionView} fileDef 保存先データ項目
  * @param {Array<NewQfile>} qfiles 追加保存するファイルを格納した配列
  */
function saveFiles(fileDef, qfiles) {
    let files = engine.findData(fileDef);
    if (files === null) {
        files = new java.util.ArrayList();
    }
    qfiles.forEach(qfile => {
        files.add(qfile);
    });
    engine.setData(fileDef, files);
}

    
%d bloggers like this: