首页 > 解决方案 > 如何使用服务帐户凭据将文件上传到 Google Team Drive 中的文件夹?

问题描述

我正在尝试使用 Google Drive Java API v3 为我们的应用程序实现一项新服务,该服务负责将文件上传到 Google Team Drive 中的特定文件夹。我使用我的公司专门为此项目创建的服务帐户,并且还从 Google Developer Console 生成了一个包含私钥的 JSON 文件。我还使用其电子邮件 xxxxx@xxxx.iam.gserviceaccount.com 将文件夹共享给服务帐户,并授予 Content Manager 对共享团队驱动器的权限。此外,由于某些原因,该服务帐户未授予 G Suite 域范围的权限。

我想在这里实现什么:
我想使用服务帐户生成的私钥构建并返回授权的 Google Drive 客户端服务,因此能够发送将文件上传到 Google Team Drive 中的文件夹的请求。

我目前使用的:

问题是什么:
我无法成功返回授权的 Google Drive 客户端服务,并且根本没有发送上传文件的请求。更令人困惑的是没有抛出异常。但是,凭据会成功返回,并带有访问令牌和过期时间。

我已经阅读/发现的内容:
使用 OAuth2.0 进行服务器到服务器应用程序:https ://developers.google.com/identity/protocols/OAuth2ServiceAccount

关于创建对 Drive API 的简单请求的 Java 快速入门: https ://developers.google.com/drive/api/v3/quickstart/java

Drive API 的 JavaDoc 参考: https ://developers.google.com/resources/api-libraries/documentation/drive/v3/java/latest/

如何使用服务帐户凭据 将文件上传到谷歌驱动器:如何使用服务帐户凭据将文件上传到谷歌驱动器

如何使用带有 Google Drive .NET API v3 的服务帐户访问 Team Drive:如何使用带有 Google Drive .NET API v3 的服务帐户访问 Team Drive

使用适用于 Java 的 Google 驱动器 API 客户端库 在我的驱动器中上传文件的身份验证 使用适用于 Java 的 Google 驱动器 API 客户端库在我的驱动器中上传文件的身份验证

我已经尝试过的:

ContractStateUpdateService.java的相关部分:

File fileMetadata = new File();
fileMetadata.setName(fileTitle);
// setting the id of folder to which the file must be inserted to
fileMetadata.setParents(Collections.singletonList("dumbFolderId"));
fileMetadata.setMimeType("application/pdf");

byte[] pdfBytes = Base64.getDecoder().decode(base64File.getBytes(StandardCharsets.UTF_8));
InputStream inputStream = new ByteArrayInputStream(pdfBytes);

// decoding base64 to PDF and its contents to a byte array without saving the file on the file system
InputStreamContent mediaContent = new InputStreamContent("application/pdf", inputStream);

logger.info("Starting to send the request to drive api");
File file = DriveUtils.getDriveService().files().create(fileMetadata, mediaContent).execute();
logger.info("Succesfully uploaded file: " + file.getDriveId());

DriveUtils.java:

public class DriveUtils {

    private static final String APPLICATION_NAME = "Google Drive Service";

    // setting the Drive scope since it is essential to access Team Drive
    private static List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE);

    // private key is stored at the root of the project for now
    private static String PRIVATE_KEY_PATH = "/path/to/private_key.json";
    private static final Logger logger = LoggerFactory.getLogger(DriveUtils.class);

    // build and return an authorized drive client service
    public static Drive getDriveService() throws IOException, GeneralSecurityException {
        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
        GoogleCredentials credentials;

        try (FileInputStream inputStream = new FileInputStream(PRIVATE_KEY_PATH)){
            credentials = ServiceAccountCredentials.fromStream(inputStream).createScoped(SCOPES);
            credentials.refreshIfExpired();
            AccessToken token = credentials.getAccessToken();
            logger.info("credentials: " + token.getTokenValue());
        } catch (FileNotFoundException ex) {
            logger.error("File not found: {}", PRIVATE_KEY_PATH);
            throw new FileNotFoundException("File not found: " + ex.getMessage());
        }

        logger.info("Instantiating client next");
        // Instantiating a client: this is where the client should be built but nothing happens... no exceptions!
        Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, (HttpRequestInitializer) credentials)
                .setApplicationName(APPLICATION_NAME)
                .build();
        // this log should appear immediately after the client has been instantiated but still nothing happens
        logger.info("Client instantiated");

        return service;
    }

}

pom.xml:

<!-- https://mvnrepository.com/artifact/com.google.api-client/google-api-client -->
        <dependency>
            <groupId>com.google.api-client</groupId>
            <artifactId>google-api-client</artifactId>
            <version>1.29.2</version>
        </dependency>

        <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-drive</artifactId>
            <version>v3-rev165-1.25.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.google.auth/google-auth-library-oauth2-http -->
        <dependency>
            <groupId>com.google.auth</groupId>
            <artifactId>google-auth-library-oauth2-http</artifactId>
            <version>0.16.1</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.6.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.google.oauth-client/google-oauth-client-jetty -->
        <dependency>
            <groupId>com.google.oauth-client</groupId>
            <artifactId>google-oauth-client-jetty</artifactId>
            <version>1.29.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

我确定我在这里遗漏了一些东西,我提前为我的英语道歉。任何帮助将不胜感激。

标签: javaspring-bootgoogle-drive-apigoogle-api-java-clientservice-accounts

解决方案


感谢您的评论,这里的建议很有帮助,值得研究。但是,我将在此处介绍的解决方案不会直接回答我的问题,即我的代码如何或为什么没有产生任何错误消息。所以现在,这是我解决这个问题的方法:

  1. 启用 Drive API。在阅读了有关从服务帐户向 Drive API 发出请求的文章和文档的大部分内容后,很明显,如果我们没有从 Google API 控制台启用 Drive API,我的代码将无法正常工作。
  2. 将三个依赖的版本降级到1.23.0。

pom.xml:

 <dependency>
    <groupId>com.google.api-client</groupId>
    <artifactId>google-api-client</artifactId>
    <version>1.23.0</version>
 </dependency>
 <dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-drive</artifactId>
    <version>v3-rev110-1.23.0</version>
 </dependency>
 <dependency>
    <groupId>com.google.oauth-client</groupId>
    <artifactId>google-oauth-client-jetty</artifactId>
    <version>1.23.0</version>
 </dependency>
  1. 将属性的值设置setSupportsTeamDrive为 true。如果没有该属性,我们根本无法将文件保存到团队云端硬盘中的共享文件夹中。

ContractStateUpdateService.java:

File fileMetadata = new File();
fileMetadata.setName(fileTitle);

// setting the id of folder to which the file must be inserted to
fileMetadata.setParents(Collections.singletonList("dumbTeamDriveId"));
fileMetadata.setMimeType("application/pdf");

// decoding base64 to PDF and its contents to a byte array without saving the file on the file system
byte[] pdfBytes = Base64.getDecoder().decode(base64File.getBytes(StandardCharsets.UTF_8);
InputStream inputStream = new ByteArrayInputStream(pdfBytes);
InputStreamContent mediaContent = new InputStreamContent("application/pdf", inputStream);

try {
  // upload updated agreement as a PDF file to the Team Drive folder
  DriveUtils.getDriveService().files().create(fileMetadata, mediaContent)
                            .setSupportsTeamDrives(true) // remember to set this property to true!
                            .execute();
} catch (IOException ex) {
  logger.error("Exception: {}", ex.getMessage());
  throw new IOException("Exception: " + ex.getMessage());
} catch (GeneralSecurityException ex) {
  logger.error("Exception: {}", ex.getMessage());
  throw new GeneralSecurityException("Exception: " + ex.getMessage());
}
  1. 拆分方法使逻辑更清晰。

从DriveUtils-class更新代码:

// create and return credential
private static Credential getCredentials() throws IOException {
    GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(PRIVATE_KEY_PATH))
                .createScoped(SCOPES);

    return credential;
}

// build and return an authorized drive client service
public static Drive getDriveService() throws IOException, GeneralSecurityException {
    final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

    // Instantiating a client
    Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials())
                .setApplicationName(APPLICATION_NAME)
                .build();

    return service;
}

推荐阅读