将 Google 表单中的文件上传到 Google 云端硬盘

编码级别:新手
时长:10 分钟
项目类型:通过事件驱动型触发器实现自动化

目标

  • 了解解决方案的用途。
  • 了解 Apps 脚本服务在此解决方案中执行的操作。
  • 设置脚本。
  • 运行脚本。

关于此解决方案

使用 Google 表单在 Google 云端硬盘中同时上传和整理文件。 该表单包含要上传的文件以及应如何组织文件的输入。

用于上传文件的表单的屏幕截图

运作方式

设置函数会创建一个用于存储所有上传文件的文件夹,以及一个在每次用户提交表单时触发的触发器。用户填写表单时,需要选择要上传的文件以及一个用于存储这些文件的子文件夹。用户提交表单后,脚本会将文件路由到相应的子文件夹。如果该文件夹尚不存在,脚本会创建它。

Apps 脚本服务

此解决方案使用以下服务:

  • 脚本服务 - 创建在每次用户提交表单时触发的触发器。
  • 属性服务 - 存储脚本在设置期间创建的触发器的 ID,以防止出现重复的触发器。
  • 云端硬盘服务 - 在设置过程中,会获取表单在云端硬盘中的位置,并在同一位置创建一个文件夹。当用户提交表单时,云端硬盘服务会将文件路由到该文件夹,如果选择该文件夹,则会路由到指定的子文件夹。如果子文件夹不存在,脚本会创建该子文件夹。
  • 表单服务 - 获取用户在提交表单并将其发送到云端硬盘服务后选择的文件和文件夹名称。

前提条件

如需使用此示例,您需要满足以下前提条件:

  • Google 帐号(Google Workspace 帐号可能需要管理员批准)。
  • 一个能够访问互联网的网络浏览器。

设置脚本

创建表单

  1. 转到 forms.google.com,然后点击“空白”图标
  2. 点击无标题表单,然后将表单重命名为将文件上传到云端硬盘
  3. 点击未命名的问题,然后将问题重命名为子文件夹
  4. 子文件夹问题上,点击“更多”图标 > 说明
  5. 说明部分,输入选择要存储文件的子文件夹。如果选择 <None>,文件将存储在“Uploaded files”文件夹中。
  6. 将以下选项添加到子文件夹问题中:
    • <无>
    • 项目 A
    • 项目 B
    • 项目 C
  7. 要将题目设为必答,请点击必填
  8. 点击“添加问题”图标
  9. 点击单选题,然后选择上传文件
  10. 点击继续
  11. 题目部分,输入要上传的文件。您可以选择 允许用户上传的文件类型和文件数量上限
  12. 要将题目设为必答,请点击必填

创建 Apps 脚本项目

  1. 在表单中,依次点击“更多”图标 > 脚本编辑器
  2. 点击未命名项目,然后将项目重命名为将文件上传到云端硬盘
  3. 如需创建其他脚本文件,请点击“Add a file” > Script。将文件命名为 Setup
  4. 将这两个脚本文件的内容替换为以下内容:

    Code.gs

    solutions/automations/upload-files/Code.js
    // TODO Before you start using this sample, you must run the setUp() 
    // function in the Setup.gs file.
    
    // Application constants
    const APP_TITLE = "Upload files to Drive from Forms";
    const APP_FOLDER_NAME = "Upload files to Drive (File responses)";
    
    // Identifies the subfolder form item
    const APP_SUBFOLDER_ITEM = "Subfolder";
    const APP_SUBFOLDER_NONE = "<None>";
    
    
    /**
     * Gets the file uploads from a form response and moves files to the corresponding subfolder.
     *  
     * @param {object} event - Form submit.
     */
    function onFormSubmit(e) {
      try {
        // Gets the application root folder.
        var destFolder = getFolder_(APP_FOLDER_NAME);
    
        // Gets all form responses.
        let itemResponses = e.response.getItemResponses();
    
        // Determines the subfolder to route the file to, if any.
        var subFolderName;
        let dest = itemResponses.filter((itemResponse) =>
          itemResponse.getItem().getTitle().toString() === APP_SUBFOLDER_ITEM);
    
        // Gets the destination subfolder name, but ignores if APP_SUBFOLDER_NONE was selected;
        if (dest.length > 0) {
          if (dest[0].getResponse() != APP_SUBFOLDER_NONE) {
            subFolderName = dest[0].getResponse();
          }
        }
        // Gets the subfolder or creates it if it doesn't exist.
        if (subFolderName != undefined) {
          destFolder = getSubFolder_(destFolder, subFolderName)
        }
        console.log(`Destination folder to use:
        Name: ${destFolder.getName()}
        ID: ${destFolder.getId()}
        URL: ${destFolder.getUrl()}`)
    
        // Gets the file upload response as an array to allow for multiple files.
        let fileUploads = itemResponses.filter((itemResponse) => itemResponse.getItem().getType().toString() === "FILE_UPLOAD")
          .map((itemResponse) => itemResponse.getResponse())
          .reduce((a, b) => [...a, ...b], []);
    
        // Moves the files to the destination folder.
        if (fileUploads.length > 0) {
          fileUploads.forEach((fileId) => {
            DriveApp.getFileById(fileId).moveTo(destFolder);
            console.log(`File Copied: ${fileId}`)
          });
        }
      }
      catch (err) {
        console.log(err);
      }
    }
    
    
    /**
     * Returns a Drive folder under the passed in objParentFolder parent
     * folder. Checks if folder of same name exists before creating, returning 
     * the existing folder or the newly created one if not found.
     *
     * @param {object} objParentFolder - Drive folder as an object.
     * @param {string} subFolderName - Name of subfolder to create/return.
     * @return {object} Drive folder
     */
    function getSubFolder_(objParentFolder, subFolderName) {
    
      // Iterates subfolders of parent folder to check if folder already exists.
      const subFolders = objParentFolder.getFolders();
      while (subFolders.hasNext()) {
        let folder = subFolders.next();
    
        // Returns the existing folder if found.
        if (folder.getName() === subFolderName) {
          return folder;
        }
      }
      // Creates a new folder if one doesn't already exist.
      return objParentFolder.createFolder(subFolderName)
        .setDescription(`Created by ${APP_TITLE} application to store uploaded Forms files.`);
    }
    

    Setup.gs

    solutions/automations/upload-files/Setup.js
    // TODO You must run the setUp() function before you start using this sample.
    
    /** 
     * The setUp() function performs the following:
     *  - Creates a Google Drive folder named by the APP_FOLDER_NAME
     *    variable in the Code.gs file.
     *  - Creates a trigger to handle onFormSubmit events.
     */
    function setUp() {
      // Ensures the root destination folder exists.
      const appFolder = getFolder_(APP_FOLDER_NAME);
      if (appFolder !== null) {
        console.log(`Application folder setup.
        Name: ${appFolder.getName()}
        ID: ${appFolder.getId()}
        URL: ${appFolder.getUrl()}`)
      }
      else {
        console.log(`Could not setup application folder.`)
      }
      // Calls the function that creates the Forms onSubmit trigger.
      installTrigger_();
    }
    
    /** 
     * Returns a folder to store uploaded files in the same location
     * in Drive where the form is located. First, it checks if the folder
     * already exists, and creates it if it doesn't.
     *
     * @param {string} folderName - Name of the Drive folder. 
     * @return {object} Google Drive Folder
     */
    function getFolder_(folderName) {
    
      // Gets the Drive folder where the form is located.
      const ssId = FormApp.getActiveForm().getId();
      const parentFolder = DriveApp.getFileById(ssId).getParents().next();
    
      // Iterates through the subfolders to check if folder already exists.
      // The script checks for the folder name specified in the APP_FOLDER_NAME variable.
      const subFolders = parentFolder.getFolders();
      while (subFolders.hasNext()) {
        let folder = subFolders.next();
    
        // Returns the existing folder if found.
        if (folder.getName() === folderName) {
          return folder;
        }
      }
      // Creates a new folder if one doesn't already exist.
      return parentFolder.createFolder(folderName)
        .setDescription(`Created by ${APP_TITLE} application to store uploaded files.`);
    }
    
    /**
     * Installs trigger to capture onFormSubmit event when a form is submitted.
     * Ensures that the trigger is only installed once.
     * Called by setup().
     */
    function installTrigger_() {
      // Ensures existing trigger doesn't already exist.
      let propTriggerId = PropertiesService.getScriptProperties().getProperty('triggerUniqueId')
      if (propTriggerId !== null) {
        const triggers = ScriptApp.getProjectTriggers();
        for (let t in triggers) {
          if (triggers[t].getUniqueId() === propTriggerId) {
            console.log(`Trigger with the following unique ID already exists: ${propTriggerId}`);
            return;
          }
        }
      }
      // Creates the trigger if one doesn't exist.
      let triggerUniqueId = ScriptApp.newTrigger('onFormSubmit')
        .forForm(FormApp.getActiveForm())
        .onFormSubmit()
        .create()
        .getUniqueId();
      PropertiesService.getScriptProperties().setProperty('triggerUniqueId', triggerUniqueId);
      console.log(`Trigger with the following unique ID was created: ${triggerUniqueId}`);
    }
    
    /**
     * Removes all script properties and triggers for the project.
     * Use primarily to test setup routines.
     */
    function removeTriggersAndScriptProperties() {
      PropertiesService.getScriptProperties().deleteAllProperties();
      // Removes all triggers associated with project.
      const triggers = ScriptApp.getProjectTriggers();
      for (let t in triggers) {
        ScriptApp.deleteTrigger(triggers[t]);
      }
    }
    
    /**
     * Removes all form responses to reset the form.
     */
    function deleteAllResponses() {
      FormApp.getActiveForm().deleteAllResponses();
    }
    

运行脚本

  1. 在 Apps 脚本编辑器中,切换到 Setup.gs 文件。
  2. 在函数下拉菜单中,选择 setUp
  3. 点击运行
  4. 出现提示时,授权脚本。如果 OAuth 同意屏幕显示 This app is not verified(此应用未经验证)警告,请依次选择 Advanced > Go to {Project Name} (unsafe),继续操作。

  5. 返回表单,然后点击“预览”预览图标

  6. 在表单上,选择一个子文件夹并上传文件。

  7. 点击提交

  8. 前往云端硬盘,打开将文件上传到云端硬盘(文件响应)文件夹。您上传的文件位于您在表单上选择的子文件夹中。

贡献者

此示例由 Google 在 Google 开发者专家的帮助下进行维护。

后续步骤