Box: ファイルを PDF/テキスト/画像でダウンロード (Box: Download File as PDF/Text/Image)
この工程は、Box 上のファイルを指定形式でダウンロードします。
Configs:共通設定
  • 工程名
  • メモ
Configs
  • C1: OAuth2 設定 *
  • C2: ダウンロードするファイルの ID *
  • C3: ダウンロードするファイル形式 *
  • C4: ファイル名 (空白の場合、Box でのファイル名を使用します)#{EL}
  • C5: ダウンロードファイルを追加保存するファイル型データ項目 *

Notes

  • Box のリフレッシュトークンには、期限があります。期限を超えないよう、定期的に利用する必要があります。
  • このアイテムは、Box ファイルのレプリゼンテーション API を使用しています。サポートされているファイルの種類については、Box 開発者向けドキュメントを参照してください。
  • JPEG (2048×2048)、PNG (1024×1024)、PNG (2048×2048) のようにページ毎にファイルがダウンロードされる形式の場合、ダウンロードファイルのファイル名の先頭にページ番号が付与されます。
  • ファイルをそのファイル形式ではじめてダウンロードする際、「The representation [xxx] is being created and not available yet. Please try again later.」というスクリプトエラーが起こることがあります。その場合、しばらく後にこの工程を再実行すると、ダウンロードに成功します。

Capture

See also

Script (click to open)
  • 下記のスクリプトを記述した XML ファイルをダウンロードできます
    • box-file-representation-download.xml (C) Questetra, Inc. (MIT License)
    • Professional をご利用であればファイルの内容を改変することでオリジナルのアドオンとして活用できます


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人のブロガーが「いいね」をつけました。