Google Drive: Download File

Google Drive: Download File

Google ドライブ: ファイルダウンロード

This item downloads the specified files on Google Drive. You can download multiple files at once. When you download multiple files, you should write one File ID per line.

Auto Step icon
Basic Configs
Step Name
Note
Configs for this Auto Step
conf_Auth
C1: Service Account Setting *
conf_FileIds
C2: File IDs to download (Write one per line) *
conf_FileData
C3: Data item to add the downloaded file *

Notes

  • It is required that the files to download be shared in advance with the service account set in [C1: Service Account Setting]
  • To set up [C1: Service Account Setting] :
    1. Prepare a service account on Google Cloud Console
    2. Create an OAuth2 JWT Bearer setting on Questetra BPM Suite and set it to C1
      • Scope https://www.googleapis.com/auth/drive.readonly is required
      • Set up other items as shown in the table below :
Settings on Questetra BPM SuiteCorresponding Information
of Google Cloud Service Account Key
Property Name in JSON FileRequired or Not
Client IDOAuth2 Client IDclient_idNot Required
Private Key IDKey IDprivate_key_idRequired
Private KeyPrivate Keyprivate_keyRequired
Custom Secret Information 1Email Addressclient_emailRequired

Errors

  • "code": 403, "message": "Google Drive API has not been used in project 123456789012 before or it is disabled.".
    • ‘Google Drive API’ needs to be enabled
  • "code": 404, "message": "File not found:"
    • The file may not exist
    • ‘Google Service Account’ may not have permission to read the file

Capture

See Also

Script (click to open)
  • An XML file that contains the code below is available to download
    • google-drive-file-download.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


const REQUEST_NUM_PER_FILE = 4; // ファイル 1 つにつき 4 回リクエストを行う

function main() {
    ////// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
    const auth = configs.getObject('conf_Auth');
    const fileIds = retrieveFileIds();

    const fileDef = configs.getObject('conf_FileData');
    let files = engine.findData( fileDef );
    if (files === null) {
        files = new java.util.ArrayList();
    }

    ////// == 演算 / Calculating ==
    if (fileIds.length * REQUEST_NUM_PER_FILE > httpClient.getRequestingLimit()) {
        throw new Error('Number of File IDs exceeds the limit.');
    }
    fileIds.forEach(fileId => {
        const {name, contentType} = getFileMetadata(auth, fileId);
        const content = getFile(auth, fileId);
        const qfile = new com.questetra.bpms.core.event.scripttask.NewQfile(
            name, contentType, content
        );
        files.add(qfile);
    });

    ////// == ワークフローデータへの代入 / Data Updating ==
    engine.setData(fileDef, files);
}

/**
 * config からファイル ID を読み出す
 * ファイル ID が設定されていない場合はエラー
 * @returns {Array<String>} ファイル ID の配列
 */
const retrieveFileIds = () => {
    let fileIdsStr = configs.get('conf_FileIds');
    const fileIdsDef = configs.getObject('conf_FileIds');
    if (fileIdsDef !== null) {
        fileIdsStr = engine.findData(fileIdsDef);
    }
    if (fileIdsStr === null) {
        throw new Error('No File IDs.');
    }
    const fileIds =  fileIdsStr.split('\n').filter(key => key.length !== 0);
    if (fileIds.length === 0) {
        throw new Error('No File IDs.');
    }
    return fileIds;
};

const URL_TOKEN_REQUEST = 'https://oauth2.googleapis.com/token';
const SCOPE = 'https://www.googleapis.com/auth/drive.readonly';

/**
 * @param auth HTTP 認証設定
 * @returns {any} アクセストークンを含むオブジェクト
 */
const getAccessToken = (auth) => {
    const privateKeyId = auth.getPrivateKeyId();
    const privateKey = auth.getPrivateKey();
    const serviceAccount = auth.getCustomSecret1();
    const scope = auth.getScope();
    if (!scope.split(' ').includes(SCOPE)) {
        throw new Error(`Scope ${SCOPE} must be included in the scope.`);
    }
    if (privateKeyId === '') {
        throw new Error('Private Key ID is required.');
    }
    if (privateKey === '') {
        throw new Error('Private Key is required.');
    }
    if (serviceAccount === '') {
        throw new Error('Service Account must be set to Custom Secret 1.');
    }
    const header = {
        "alg": "RS256",
        "typ": "at+jwt",
        "kid": privateKeyId
    };
    const now = Math.floor(Date.now() / 1000);
    const payload = {
        "iss": serviceAccount,
        "aud": URL_TOKEN_REQUEST,
        "sub": '',
        "iat": now,
        "exp": now + 3600,
        /**
         * https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth
         * "without OAuth" の話だが、OAuth でも 1 hour になるようだ。
         * 1 hour より長ければエラー。短ければ、1 hour のトークンが返ってくる。
         */
        scope
    };
    const keyB = rsa.readKeyFromPkcs8(privateKey);
    const assertion = jwt.build(header, payload, keyB);

    const response = httpClient.begin()
        .formParam("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
        .formParam('assertion', assertion)
        .post(URL_TOKEN_REQUEST);
    const responseText = response.getResponseAsString();
    if (response.getStatusCode() !== 200) {
        engine.log(responseText);
        throw new Error(`Failed to get Access token. status: ${response.getStatusCode()}`);
    }
    const result = JSON.parse(response.getResponseAsString());
    if (result.access_token === undefined) {
        engine.log(responseText);
        throw new Error(`Failed to get Access token. access token not found.`);
    }
    return result;
};

/**
 * ファイルメタデータ取得
 * @param auth HTTP 認証設定
 * @param fileId ファイルID
 */
const getFileMetadata = (auth, fileId) => {
    const URL = `https://www.googleapis.com/drive/v3/files/${encodeURIComponent(fileId)}`;

    const response = httpClient.begin()
        .oauth2JwtBearer(auth, () => getAccessToken(auth))
        .queryParam("supportsAllDrives", "true")
        .get(URL);

    const status = response.getStatusCode();
    const respTxt = response.getResponseAsString();
    if (status !== 200) {
        engine.log(respTxt);
        throw new Error(`Failed to get metadata of file. status: ${status}`);
    }
    const respJson = JSON.parse(respTxt);
    return {
        name: respJson.name,
        contentType: respJson.mimeType
    };
};

/**
 * ファイル取得
 * @param auth HTTP 認証設定
 * @param fileId ファイルID
 */
const getFile = (auth, fileId) => {
    const URL = `https://www.googleapis.com/drive/v3/files/${encodeURIComponent(fileId)}`;

    const response = httpClient.begin()
        .oauth2JwtBearer(auth, () => getAccessToken(auth))
        .queryParam("supportsAllDrives", "true")
        .queryParam("alt", "media")
        .get(URL);

    const status = response.getStatusCode();
    const respTxt = response.getResponseAsString();
    if (status !== 200) {
        engine.log(respTxt);
        throw new Error(`Failed to download file. status: ${status}`);
    }
    return response.getResponse();
};
Scroll to Top

Discover more from Questetra Support

Subscribe now to keep reading and get access to the full archive.

Continue reading