
Google Drive: Export Google Workspace File as PDF
Google ドライブ: Google Workspace ファイル PDF エクスポート
This item downloads the specified Google Workspace files (such as Google Docs, Sheets, or Slides) in PDF format. You can download multiple files at once. When you download multiple files, you should write one File ID per line.
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 files *
Notes
- The files that will be downloaded must be shared in advance with the service account set in [C1: Service Account Setting]
- To set up [C1: Service Account Setting] :
- Prepare a service account on Google Cloud Console
- Create or upload a service account key
- If you create a service account key on Google Cloud Console, you can download a JSON file containing the necessary information
- Create or upload a service account key
- Create an OAuth2 JWT Bearer setting on Questetra BPM Suite and set it to C1
- Scope
https://www.googleapis.com/auth/drive.readonlyis required - Set up other items as shown in the table below :
- Scope
- Prepare a service account on Google Cloud Console
| Settings on Questetra BPM Suite | Corresponding Information of Google Cloud Service Account Key | Property Name in JSON File | Required or Not |
|---|---|---|---|
| Client ID | OAuth2 Client ID | client_id | Not Required |
| Private Key ID | Key ID | private_key_id | Required |
| Private Key | Private Key | private_key | Required |
| Custom Secret Information 1 | Email Address | client_email | Required |

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-export.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; // Make 4 requests per file
const CONTENT_TYPE = 'application/pdf';
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 originalFileName = getFileMetadata(auth, fileId);
const saveAs = `${originalFileName}.pdf`;
const content = exportFile(auth, fileId);
const qfile = new com.questetra.bpms.core.event.scripttask.NewQfile(
saveAs, CONTENT_TYPE, content
);
files.add(qfile);
});
////// == ワークフローデータへの代入 / Data Updating ==
engine.setData(fileDef, files);
}
/**
* Read the file ID from the config
* Error if file ID is not set.
* @returns {Array<String>} an array of file IDs
*/
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 Authentication Settings
* @returns {any} an object containing the access token
*/
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”, but it seems to be 1 hour even with OAuth.
* If it is longer than 1 hour, an error occurs. If it is shorter, a 1 hour token is returned.
*/
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;
};
/**
* Get File Metadata
* @param auth HTTP Authentication Settings
* @param fileId File ID
* @returns {String} File name
*/
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")
.queryParam("fields", "name") // Get file name only
.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 respJson.name;
};
/**
* File Export
* @param auth HTTP Authentication Settings
* @param fileId File ID
* @returns {ByteArrayWrapper} The binary data of the file
*/
const exportFile = (auth, fileId) => {
const URL = `https://www.googleapis.com/drive/v3/files/${encodeURIComponent(fileId)}/export`;
const response = httpClient.begin()
.oauth2JwtBearer(auth, () => getAccessToken(auth))
.queryParam("mimeType", CONTENT_TYPE)
.get(URL);
const status = response.getStatusCode();
const respTxt = response.getResponseAsString();
if (status !== 200) {
engine.log(respTxt);
throw new Error(`Failed to export file. status: ${status}`);
}
return response.getResponse();
};