You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用服务账号调用Google Drive API files.list无法获取子文件夹

问题描述

运行Node.js API访问Google Drive,因限制无法使用OAuth,改用服务账号认证,但出现以下异常:

  • 相同逻辑下,OAuth认证可正常调用files.list()获取子目录,服务账号认证无法获取
  • 已确认目标父文件夹及子目录均给服务账号邮箱配置了编辑权限
  • 调用files.list()时的异常现象:
    • 省略q参数可返回所有文件(含回收站文件),但仍无目录
    • 设置q: 'trashed=false',仍返回回收站文件
    • 设置q: "mimeType = 'application/vnd.google-apps.folder'",无法获取任何文件夹
    • 设置q: '${req.body.folderId}' in parents,无任何子文件夹返回

目标需求:获取服务账号有权限的顶层文件夹中非回收站的所有项(含子目录),不限创建者。

相关代码:

const authenticateGdrive = async () => {
  const serviceAccountKey = await readFile(SERVICE_ACCOUNT_KEY_PATH, 'utf8');
  const credentials = JSON.parse(serviceAccountKey);

  const auth = new google.auth.GoogleAuth({
    credentials,
    scopes: ['https://www.googleapis.com/auth/drive.file'],
  });

  return google.drive({ version: 'v3', auth });
};

export const list = async(req, res, next) => {
  try {
    const drive = await authenticateGdrive();
    // problems observed when using a service account for authentication:
    // q: `'${req.body.folderId}' in parents` does not return any subfolder
    // q: 'trashed=false', // pulls in trash anyway, but no q parameter does pull in trash
    // q: "mimeType = 'application/vnd.google-apps.folder'", // doesn't retrieve any folders
    const response = await drive.files.list({
      q: `'${req.body.folderId}' in parents`,
      fields: 'files(id, name, mimeType)',
    });

    const files = response.data.files;
    if (files.length) {
      res.status(200).send(files);
    } else {
      res.status(500).send({message: "No files retrieved"});
    };
  } catch (error) {
    res.status(500).send({message: error.message});
  };
};

问题排查与解决

1. 权限范围配置错误

当前使用的https://www.googleapis.com/auth/drive.file范围存在限制:

  • 服务账号仅能通过此范围访问自身创建或通过API上传的文件/文件夹,无法访问其他用户创建但共享给它的资源
  • 替换为https://www.googleapis.com/auth/drive(全权限)或https://www.googleapis.com/auth/drive.readonly(只读权限,满足需求的话更安全),才能让服务账号访问所有已获得权限的资源

2. 查询参数q的逻辑优化

需要组合多个条件匹配需求,正确的q参数应为:

q: `'${req.body.folderId}' in parents AND trashed=false`
  • '${req.body.folderId}' in parents:限定目标父文件夹下的资源
  • trashed=false:排除回收站中的资源
    如果需要单独筛选文件夹,可追加条件:
q: `'${req.body.folderId}' in parents AND trashed=false AND mimeType='application/vnd.google-apps.folder'`

3. 代码调整示例

修改认证函数的权限范围,同时更新files.list的查询参数:

const authenticateGdrive = async () => {
  const serviceAccountKey = await readFile(SERVICE_ACCOUNT_KEY_PATH, 'utf8');
  const credentials = JSON.parse(serviceAccountKey);

  const auth = new google.auth.GoogleAuth({
    credentials,
    // 替换为合适的权限范围
    scopes: ['https://www.googleapis.com/auth/drive.readonly'],
  });

  return google.drive({ version: 'v3', auth });
};

export const list = async(req, res, next) => {
  try {
    const drive = await authenticateGdrive();
    const response = await drive.files.list({
      // 组合查询条件
      q: `'${req.body.folderId}' in parents AND trashed=false`,
      fields: 'files(id, name, mimeType)',
    });

    const files = response.data.files;
    if (files.length) {
      res.status(200).send(files);
    } else {
      // 无资源时返回200更合理,500属于服务器错误范畴
      res.status(200).send({message: "No files retrieved"});
    };
  } catch (error) {
    res.status(500).send({message: error.message});
  };
};

额外说明

  • 之前trashed=false无效的原因是权限范围限制,返回的是服务账号自身的回收站资源,而非共享文件夹内的内容
  • 再次确认服务账号邮箱已被添加为目标文件夹的协作者(编辑权限),且文件夹未被设置共享限制

内容的提问来源于stack exchange,提问作者CindyLuWho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 03:16:26