共享文件、文件夹和云端硬盘

每个 Google 云端硬盘文件、文件夹和共享云端硬盘都关联了 permissions 资源。每项资源都用于标识特定 type(user、group、domain、anyone)和 role(owner、organizer、fileOrganizer、writer、commenter、reader)的权限。例如,某项权限可能授予特定用户 (type=user) 对文件的只读访问权限 (role=reader),而另一项权限则授予特定群组 (type=group) 的成员向文件添加评论的权限 (role=commenter)。

如需查看角色的完整列表以及每个角色允许的操作,请参阅角色和权限。

权限的传播方式

权限会从父文件夹向下传播到所有子项:

  • 默认继承:所有子文件和文件夹都会自动继承其父文件夹的权限。
  • 无法在子项上减少:您无法移除或减少子项上继承的权限。必须在原始父级文件夹中进行更改,或者文件夹必须使用受限访问权限设置。
  • 可扩展到子级:子级项目可以授予更宽松的角色,例如在用户拥有 role=reader 的文件夹中授予对文件的 role=writer。
  • 移动时重新评估:将项目移动到新的父文件夹时,系统会重新评估并将新父文件夹的权限应用于该项目及其子项。

文件链接和访问权限控制

当您与特定用户或群组共享文件或文件夹时,用于访问相应内容的网址不会更改,也不会为每位用户生成唯一链接。相反,该商品具有基于其 fileId 的单个恒定链接。

Google 云端硬盘通过评估项目的 ACL 来控制访问权限。当用户尝试打开链接时,Google 云端硬盘会根据 ACL 验证其经过身份验证的身份。如果某项权限被撤消或达到其到期日期,系统会从 ACL 中移除相应用户。如果用户尝试再次访问该链接,云端硬盘会拒绝访问。

了解文件功能

permissions 资源定义了谁有权访问(ACL),但不会直接指明当前用户是否可以在应用界面中执行特定操作。

相反,files 资源包含一组布尔值 capabilities 字段(例如 canComment、canShare 或 canDelete),这些字段由 Google Drive API 根据用户的角色和项目设置动态计算得出。

获取文件功能

在渲染应用界面时,请检查 files.capabilities,而不是直接解析权限:

  • 使用 fields=capabilities 调用 files.get 方法。如需了解详情,请参阅返回特定字段。
  • 使用返回的布尔值标志在界面中启用或停用相应操作。例如,如果 canComment 为 false,则禁止评论。

共享云端硬盘资源的使用场景

下表显示了在不同位置和项目类型之间共享云端硬盘资源所需的角色和条件:

位置 项 所需的角色 主要限制
我的云端硬盘 文件或文件夹 owner 或 writer 如果 writersCanShare=false,则需要 owner。
文件夹的限时访问权限需要 reader(请参阅设置失效日期)。
共享云端硬盘 文件 organizer、fileOrganizer 或 writer writersCanShare 始终被视为 true。
共享云端硬盘 文件夹 organizer 如果 sharingFoldersRequiresOrganizerPermission 为 false,fileOrganizer 也可以共享。
共享云端硬盘 会员资格 organizer 仅适用于 user 或 group(不适用于网域)。

管理权限

下表总结了 permissions 资源上可用的方法:

方法 API 端点 关键形参 参考
创建 POST https://www.googleapis.com/drive/v3/files/{fileId}/permissions role、type、emailAddress或domain permissions.create
获取 GET https://www.googleapis.com/drive/v3/files/{fileId}/permissions/{permissionId} fields permissions.get
列表 GET https://www.googleapis.com/drive/v3/files/{fileId}/permissions pageSize、supportsAllDrives、pageToken permissions.list
更新 PATCH https://www.googleapis.com/drive/v3/files/{fileId}/permissions/{permissionId} role、allowFileDiscovery permissions.update
删除 DELETE https://www.googleapis.com/drive/v3/files/{fileId}/permissions/{permissionId} supportsAllDrives permissions.delete

创建权限

如需共享文件、文件夹或共享云端硬盘,请对 permissions 资源调用 create 方法,并使用 fileId。创建权限会向相应项目添加新的 ACL 条目,并返回分配的 permissionId。

在请求正文中,提供以下字段:

  • role:要授予的访问权限级别(例如 reader、commenter 或 writer)。如需查看完整列表,请参阅角色和权限。
  • type:被授予者的范围(user、group、domain 或 anyone)。
  • 被授予者的标识符(根据 type 的值而定):
    • emailAddress:当 type 为 user 或 group 时,此字段为必需字段。
    • domain:当 type 为 domain 时为必需。

以下代码示例展示了如何创建权限。响应会返回 permissions 资源的实例,包括分配的 permissionId。

请求

POST https://www.googleapis.com/drive/v3/files/FILE_ID/permissions
{
  "role": "commenter",
  "type": "user",
  "emailAddress": "alex@altostrat.com"
}

答案

{
  "kind": "drive#permission",
  "id": "PERMISSION_ID",
  "type": "user",
  "role": "commenter"
}

与目标对象群组共享

目标对象群组是您可以推荐给用户,供其与之共享内容的用户群组(例如部门或团队)。您可以鼓励用户与特定或部分对象共享内容,而不是与整个组织共享。目标对象群组可帮助您提高数据的安全性与隐私性,并让用户更轻松地选择合适的内容共享对象。

如需与目标受众群体共享,请将 type=domain 设置为 ,并将 domain 设置为 <TARGET_AUDIENCE_ID>.audience.googledomains.com。如需详细了解如何在 Google 管理控制台中查找或创建目标对象群组,请参阅目标对象群组简介。

如需了解用户如何与目标受众群体互动,请参阅链接分享的用户体验。

获取权限

如需获取权限,请使用 fileId 和 permissionId 路径形参对 permissions 资源调用 get 方法。如果您不知道权限 ID,请先列出所有权限。

列出权限

如需列出文件、文件夹或共享云端硬盘的权限,请对 permissions 资源调用 list 方法,并提供必需的 fileId 路径参数。

您可以添加以下任意可选查询参数来对响应进行分页或过滤:

  • pageSize(可选):每页要返回的权限数量上限。 如果未针对共享云端硬盘中的文件设置,则最多返回 100 个结果。如果未针对不在共享云端硬盘中的文件设置此参数,则返回整个列表。

  • pageToken(可选):之前列表调用中的页面令牌,用于检索后续页面。

  • supportsAllDrives(可选):请求的应用是否同时支持“我的云端硬盘”和共享云端硬盘。

  • useDomainAdminAccess(可选):设置为 true 可将请求作为网域管理员发出。如果 fileId 参数是指共享云端硬盘,并且请求者是该共享云端硬盘所属网域的管理员,则系统会向请求者授予访问权限。如需了解详情,请参阅以网域管理员身份管理共享云端硬盘。

  • includePermissionsForView(可选):要在响应中包含的其他查看权限。仅支持 published。

  • fields(可选):要在响应中返回的特定字段。默认情况下,list 仅返回 id、type、kind 和 role。如需返回其他字段(例如 permissionDetails),请使用此参数指定这些字段。如需了解详情,请参阅返回特定字段。

确定角色来源

如需更改文件或文件夹的角色,您必须知道该角色的来源。 对于共享云端硬盘,角色的来源可能基于共享云端硬盘的成员身份、文件夹的角色或文件的角色。

如需确定共享云端硬盘或该云端硬盘中各项内容的角色来源,请对 permissions 资源调用 get 方法,并使用 fileId 和 permissionId 路径参数,以及设置为 permissionDetails 字段的 fields 参数。

如需查找 permissionId,请对 permissions 资源使用 fileId 路径参数调用 list 方法。如需提取 list 请求中的 permissionDetails 字段,请将 fields 参数设置为 permissions/permissionDetails。

此字段会列举相应用户、群组或网域的所有继承的文件权限和直接文件权限。

以下代码示例展示了如何确定角色来源。响应会返回 permissions 资源的 permissionDetails。inheritedFrom 字段提供继承权限的内容的 ID。

请求

GET https://www.googleapis.com/drive/v3/files/FILE_ID/permissions/PERMISSION_ID?fields=permissionDetails&supportsAllDrives=true

答案

{
  "permissionDetails": [
    {
      "permissionType": "member",
      "role": "commenter",
      "inheritedFrom": "INHERITED_FROM_ID",
      "inherited": true
    },
    {
      "permissionType": "file",
      "role": "writer",
      "inherited": false
    }
  ]
}

更新权限

如需更新文件或文件夹的权限,您可以更改分配的角色。如需详细了解如何查找角色来源,请参阅确定角色来源。

  1. 对 permissions 资源调用 update 方法,并将 fileId 路径参数设置为关联的文件、文件夹或共享云端硬盘,并将 permissionId 路径参数设置为要更改的权限。如需查找 permissionId,请对 permissions 资源使用 fileId 路径参数调用 list 方法。

  2. 在请求中,标识新的 role。

即使相应用户或群组已是成员,您也可以授予其对共享云端硬盘中各个文件或文件夹的权限。例如,Alex 拥有 role=commenter,这是其共享云端硬盘成员身份的一部分。不过,您的应用可以为共享云端硬盘中的文件授予 Alex role=writer 权限。在这种情况下,由于新角色的权限比通过成员身份授予的角色的权限更宽松,因此新权限将成为文件或文件夹的有效角色。

您可以通过补丁语义应用更新,这意味着您可以对资源进行部分修改。您必须在请求中明确设置要修改的字段。未包含在请求中的任何字段都会保留其现有值。如需了解详情,请参阅处理部分资源。

除了更改角色之外,当权限为 type 时,您还可以修改商品的公开范围(domain 或 anyone)。如需使共享文件可供搜索或不公开列出,请在 PATCH 请求中添加 allowFileDiscovery 布尔值字段。将此属性设置为 true 可让相应内容显示在指定受众群体的搜索结果中,即使他们未获得直接链接也是如此。您无需删除并重新创建权限即可更改此设置。

以下代码示例展示了如何将文件或文件夹的权限从 commenter 更改为 writer。响应会返回 permissions 资源的实例。

请求

PATCH https://www.googleapis.com/drive/v3/files/FILE_ID/permissions/PERMISSION_ID
{
  "role": "writer"
}

答案

{
  "kind": "drive#permission",
  "id": "PERMISSION_ID",
  "type": "user",
  "role": "writer"
}

通过批量请求更新多项权限

不支持对同一文件、文件夹或共享云端硬盘同时进行权限修改。此限制适用于所有更改操作(例如更新或删除),无论您是修改同一收件人还是不同收件人的权限,也无论请求是来自单个应用还是多个用户。

云端硬盘会以单个 ACL 的形式评估和更新项目的权限。并发操作会导致出现“最后写入获胜”的竞态条件,这可能会悄无声息地覆盖权限更改或触发 sharingRateLimitExceeded 错误。

为避免冲突,请按顺序对同一项执行权限更改,或使用批量请求在单个请求中修改多项权限。

以下示例展示了如何使用客户端库执行批量权限修改。

Java

drive/snippets/drive_v3/src/main/java/ShareFile.java
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.Permission;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/* Class to demonstrate use-case of modify permissions. */
public class ShareFile {

  /**
   * Batch permission modification.
   * realFileId file Id.
   * realUser User Id.
   * realDomain Domain of the user ID.
   *
   * @return list of modified permissions if successful, {@code null} otherwise.
   * @throws IOException if service account credentials file not found.
   */
  public static List<String> shareFile(String realFileId, String realUser, String realDomain)
      throws IOException {
        /* Load pre-authorized user credentials from the environment.
         TODO(developer) - See https://developers.google.com/identity for
         guides on implementing OAuth2 for your application.application*/
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()
        .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));
    HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(
        credentials);

    // Build a new authorized API client service.
    Drive service = new Drive.Builder(new NetHttpTransport(),
        GsonFactory.getDefaultInstance(),
        requestInitializer)
        .setApplicationName("Drive samples")
        .build();

    final List<String> ids = new ArrayList<String>();


    JsonBatchCallback<Permission> callback = new JsonBatchCallback<Permission>() {
      @Override
      public void onFailure(GoogleJsonError e,
                            HttpHeaders responseHeaders)
          throws IOException {
        // Handle error
        System.err.println(e.getMessage());
      }

      @Override
      public void onSuccess(Permission permission,
                            HttpHeaders responseHeaders)
          throws IOException {
        System.out.println("Permission ID: " + permission.getId());

        ids.add(permission.getId());

      }
    };
    BatchRequest batch = service.batch();
    Permission userPermission = new Permission()
        .setType("user")
        .setRole("writer");

    userPermission.setEmailAddress(realUser);
    try {
      service.permissions().create(realFileId, userPermission)
          .setFields("id")
          .queue(batch, callback);

      Permission domainPermission = new Permission()
          .setType("domain")
          .setRole("reader");

      domainPermission.setDomain(realDomain);

      service.permissions().create(realFileId, domainPermission)
          .setFields("id")
          .queue(batch, callback);

      batch.execute();

      return ids;
    } catch (GoogleJsonResponseException e) {
      // TODO(developer) - handle error appropriately
      System.err.println("Unable to modify permission: " + e.getDetails());
      throw e;
    }
  }
}

Python

drive/snippets/drive-v3/file_snippet/share_file.py
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def share_file(real_file_id, real_user, real_domain):
  """Batch permission modification.
  Args:
      real_file_id: file Id
      real_user: User ID
      real_domain: Domain of the user ID
  Prints modified permissions

  Load pre-authorized user credentials from the environment.
  TODO(developer) - See https://developers.google.com/identity
  for guides on implementing OAuth2 for the application.
  """
  creds, _ = google.auth.default()

  try:
    # create drive api client
    service = build("drive", "v3", credentials=creds)
    ids = []
    file_id = real_file_id

    def callback(request_id, response, exception):
      if exception:
        # Handle error
        print(exception)
      else:
        print(f"Request_Id: {request_id}")
        print(f'Permission Id: {response.get("id")}')
        ids.append(response.get("id"))

    # pylint: disable=maybe-no-member
    batch = service.new_batch_http_request(callback=callback)
    user_permission = {
        "type": "user",
        "role": "writer",
        "emailAddress": "user@example.com",
    }
    batch.add(
        service.permissions().create(
            fileId=file_id,
            body=user_permission,
            fields="id",
        )
    )
    domain_permission = {
        "type": "domain",
        "role": "reader",
        "domain": "example.com",
    }
    domain_permission["domain"] = real_domain
    batch.add(
        service.permissions().create(
            fileId=file_id,
            body=domain_permission,
            fields="id",
        )
    )
    batch.execute()

  except HttpError as error:
    print(f"An error occurred: {error}")
    ids = None

  return ids


if __name__ == "__main__":
  share_file(
      real_file_id="1dUiRSoAQKkM3a4nTPeNQWgiuau1KdQ_l",
      real_user="gduser1@workspacesamples.dev",
      real_domain="workspacesamples.dev",
  )

Node.js

drive/snippets/drive_v3/file_snippets/share_file.js
import {GoogleAuth} from 'google-auth-library';
import {google} from 'googleapis';

/**
 * Shares a file with a user and a domain.
 * @param {string} fileId The ID of the file to share.
 * @param {string} targetUserEmail The email address of the user to share with.
 * @param {string} targetDomainName The domain to share with.
 * @return {Promise<Array<string>>} A promise that resolves to an array of permission IDs.
 */
async function shareFile(fileId, targetUserEmail, targetDomainName) {
  // Authenticate with Google and get an authorized client.
  // TODO (developer): Use an appropriate auth mechanism for your app.
  const auth = new GoogleAuth({
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Drive API client (v3).
  const service = google.drive({version: 'v3', auth});

  /** @type {Array<string>} */
  const permissionIds = [];

  // The permissions to create.
  const permissions = [
    {
      type: 'user',
      role: 'writer',
      emailAddress: targetUserEmail, // e.g., 'user@partner.com'
    },
    {
      type: 'domain',
      role: 'writer',
      domain: targetDomainName, // e.g., 'example.com'
    },
  ];

  // Iterate through the permissions and create them one by one.
  for (const permission of permissions) {
    const result = await service.permissions.create({
      requestBody: permission,
      fileId,
      fields: 'id',
    });

    if (result.data.id) {
      permissionIds.push(result.data.id);
      console.log(`Inserted permission id: ${result.data.id}`);
    } else {
      throw new Error('Failed to create permission');
    }
  }
  return permissionIds;
}

PHP

drive/snippets/drive_v3/src/DriveShareFile.php
<?php
use Google\Client;
use Google\Service\Drive;
function shareFile()
{
    try {
        $client = new Client();
        $client->useApplicationDefaultCredentials();
        $client->addScope(Drive::DRIVE);
        $driveService = new Drive($client);
        $realFileId = readline("Enter File Id: ");
        $realUser = readline("Enter user email address: ");
        $realDomain = readline("Enter domain name: ");
        $ids = array();
            $fileId = '1sTWaJ_j7PkjzaBWtNc3IzovK5hQf21FbOw9yLeeLPNQ';
            $fileId = $realFileId;
            $driveService->getClient()->setUseBatch(true);
            try {
                $batch = $driveService->createBatch();

                $userPermission = new Drive\Permission(array(
                    'type' => 'user',
                    'role' => 'writer',
                    'emailAddress' => 'user@example.com'
                ));
                $userPermission['emailAddress'] = $realUser;
                $request = $driveService->permissions->create(
                    $fileId, $userPermission, array('fields' => 'id'));
                $batch->add($request, 'user');
                $domainPermission = new Drive\Permission(array(
                    'type' => 'domain',
                    'role' => 'reader',
                    'domain' => 'example.com'
                ));
                $userPermission['domain'] = $realDomain;
                $request = $driveService->permissions->create(
                    $fileId, $domainPermission, array('fields' => 'id'));
                $batch->add($request, 'domain');
                $results = $batch->execute();

                foreach ($results as $result) {
                    if ($result instanceof Google_Service_Exception) {
                        // Handle error
                        printf($result);
                    } else {
                        printf("Permission ID: %s\n", $result->id);
                        array_push($ids, $result->id);
                    }
                }
            } finally {
                $driveService->getClient()->setUseBatch(false);
            }
            return $ids;
    } catch(Exception $e) {
        echo "Error Message: ".$e;
    }

}

.NET

drive/snippets/drive_v3/DriveV3Snippets/ShareFile.cs
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Drive.v3.Data;
using Google.Apis.Requests;
using Google.Apis.Services;

namespace DriveV3Snippets
{
    // Class to demonstrate use-case of Drive modify permissions.
    public class ShareFile
    {
        /// <summary>
        /// Batch permission modification.
        /// </summary>
        /// <param name="realFileId">File id.</param>
        /// <param name="realUser">User id.</param>
        /// <param name="realDomain">Domain id.</param>
        /// <returns>list of modified permissions, null otherwise.</returns>
        public static IList<String> DriveShareFile(string realFileId, string realUser, string realDomain)
        {
            try
            {
                /* Load pre-authorized user credentials from the environment.
                 TODO(developer) - See https://developers.google.com/identity for
                 guides on implementing OAuth2 for your application. */
                GoogleCredential credential = GoogleCredential.GetApplicationDefault()
                    .CreateScoped(DriveService.Scope.Drive);

                // Create Drive API service.
                var service = new DriveService(new BaseClientService.Initializer
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Drive API Snippets"
                });

                var ids = new List<String>();
                var batch = new BatchRequest(service);
                BatchRequest.OnResponse<Permission> callback = delegate(
                    Permission permission,
                    RequestError error,
                    int index,
                    HttpResponseMessage message)
                {
                    if (error != null)
                    {
                        // Handle error
                        Console.WriteLine(error.Message);
                    }
                    else
                    {
                        Console.WriteLine("Permission ID: " + permission.Id);
                    }
                };
                Permission userPermission = new Permission()
                {
                    Type = "user",
                    Role = "writer",
                    EmailAddress = realUser
                };

                var request = service.Permissions.Create(userPermission, realFileId);
                request.Fields = "id";
                batch.Queue(request, callback);

                Permission domainPermission = new Permission()
                {
                    Type = "domain",
                    Role = "reader",
                    Domain = realDomain
                };
                request = service.Permissions.Create(domainPermission, realFileId);
                request.Fields = "id";
                batch.Queue(request, callback);
                var task = batch.ExecuteAsync();
                task.Wait();
                return ids;
            }
            catch (Exception e)
            {
                // TODO(developer) - handle error appropriately
                if (e is AggregateException)
                {
                    Console.WriteLine("Credential Not found");
                }
                else
                {
                    throw;
                }
            }
            return null;
        }
    }
}

删除权限

如需撤消对文件或文件夹的访问权限,请对 permissions 资源调用 delete 方法,并使用 fileId 和 permissionId 路径参数。

无法直接在子项上撤消继承的权限。请改为更新或删除父文件夹的权限(或使用受限访问权限设置)。

请注意,移除用户对父级项目的访问权限只会撤消从该父级项目继承的权限。如果用户还获得了对子项的直接权限,则该直接访问权限会继续保留。如需确认权限已移除,请使用 fileId 调用 list。

设置失效日期

如需授予对文件或文件夹的临时访问权限,请在调用 create 或 update 方法时设置 expirationTime 字段(RFC 3339 日期时间)。

过期时间具有以下限制:

  • 只能针对 user 和 group 权限设置此属性(不能针对 domain 或 anyone 权限设置)。
  • 时间必须是未来的时间,最长为一年。
  • 对于文件夹,仅支持使用 reader 角色的临时访问权限。