如何使用服务帐户凭据将文件上传到 Google Team Drive 中的文件夹?
How can I upload files to a folder in Google Team Drive with a service account credential?
我正在尝试使用 Google Drive Java API v3 为我们的应用程序实施一项新服务,该服务负责将文件上传到 [=120= 中的特定文件夹] 团队驱动。我使用我的公司专门为此项目创建的服务帐户,还从 Google 开发人员控制台生成了一个包含私钥的 JSON 文件。我还使用其电子邮件 xxxxx@xxxx.iam.gserviceaccount.com 将文件夹共享到服务帐户,并授予内容管理员对共享团队驱动器的权限。此外,由于某些原因,尚未向该服务帐户授予 G Suite 全域权限。
我想在这里实现什么:
我想使用服务帐户生成的私钥构建和 return 一个授权的 Google Drive 客户端服务,因此能够发送请求以将文件上传到 Google Team Drive 中的文件夹。
我目前使用的是:
- IntelliJ IDEA IntelliJ IDEA 2018.1.7(旗舰版)
- Spring 引导 2
- Java 10.0.1
问题是什么:
我无法成功 return 授权 Google 驱动客户端服务,并且根本没有发送上传文件的请求。更令人困惑的是没有抛出异常。但是,凭据 return 使用访问令牌和到期时间成功编辑。
我已经有了read/found:
将 OAuth2.0 用于服务器到服务器应用程序:https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Java 快速入门创建对驱动器的简单请求 API:
https://developers.google.com/drive/api/v3/quickstart/java
Java驱动器的文档参考 API:
https://developers.google.com/resources/api-libraries/documentation/drive/v3/java/latest/
如何使用服务帐户凭据将文件上传到 google 驱动器:
如何通过 Google Drive .NET API v3 使用服务帐户访问 Team Drive:
使用 Google 驱动器 API 客户端库为 Java 上传文件到我的驱动器的身份验证
Authentication to upload files in my drive using Google drive API client library for Java
我已经试过了:
- 使用 GoogleCredential-class 到 return 凭证 (class 似乎已被弃用:https://googleapis.dev/java/google-api-client/latest/)
- 通过查看 Github 项目和教程
寻找已弃用 class 的替代品
- 更新并检查 pom.xml
中所有缺失的依赖项
- 检查了共享团队云端硬盘的设置,确保它与服务帐户共享
- 添加日志以确保问题确实是我上面描述的问题
- 尝试了 Java 快速入门创建对驱动器的简单请求 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>
我确定我在这里遗漏了一些东西,我提前为我的英语道歉。任何帮助将不胜感激。
感谢您的评论,这里的建议很有帮助,值得研究。但是,我将在此处介绍的解决方案不会直接回答我的问题,即我的代码如何或为何不产生任何错误消息。所以现在,这是我解决问题的方法:
- 启用 Drive API. 在阅读了关于从服务帐户向 Drive API 发出请求的大部分文章和文档后,很明显如果我们没有从 Google API 控制台启用驱动器 API,我的代码将无法工作。
- 正在将三个依赖的版本降级到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>
- 将 属性
setSupportsTeamDrive
的值设置为 true。 如果没有 属性 我们将无法将文件保存到Team Drive 中的共享文件夹。
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());
}
- 方法拆分,逻辑更清晰
来自 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;
}
我正在尝试使用 Google Drive Java API v3 为我们的应用程序实施一项新服务,该服务负责将文件上传到 [=120= 中的特定文件夹] 团队驱动。我使用我的公司专门为此项目创建的服务帐户,还从 Google 开发人员控制台生成了一个包含私钥的 JSON 文件。我还使用其电子邮件 xxxxx@xxxx.iam.gserviceaccount.com 将文件夹共享到服务帐户,并授予内容管理员对共享团队驱动器的权限。此外,由于某些原因,尚未向该服务帐户授予 G Suite 全域权限。
我想在这里实现什么:
我想使用服务帐户生成的私钥构建和 return 一个授权的 Google Drive 客户端服务,因此能够发送请求以将文件上传到 Google Team Drive 中的文件夹。
我目前使用的是:
- IntelliJ IDEA IntelliJ IDEA 2018.1.7(旗舰版)
- Spring 引导 2
- Java 10.0.1
问题是什么:
我无法成功 return 授权 Google 驱动客户端服务,并且根本没有发送上传文件的请求。更令人困惑的是没有抛出异常。但是,凭据 return 使用访问令牌和到期时间成功编辑。
我已经有了read/found:
将 OAuth2.0 用于服务器到服务器应用程序:https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Java 快速入门创建对驱动器的简单请求 API: https://developers.google.com/drive/api/v3/quickstart/java
Java驱动器的文档参考 API: https://developers.google.com/resources/api-libraries/documentation/drive/v3/java/latest/
如何使用服务帐户凭据将文件上传到 google 驱动器:
如何通过 Google Drive .NET API v3 使用服务帐户访问 Team Drive:
使用 Google 驱动器 API 客户端库为 Java 上传文件到我的驱动器的身份验证 Authentication to upload files in my drive using Google drive API client library for Java
我已经试过了:
- 使用 GoogleCredential-class 到 return 凭证 (class 似乎已被弃用:https://googleapis.dev/java/google-api-client/latest/)
- 通过查看 Github 项目和教程
- 更新并检查 pom.xml
- 检查了共享团队云端硬盘的设置,确保它与服务帐户共享
- 添加日志以确保问题确实是我上面描述的问题
- 尝试了 Java 快速入门创建对驱动器的简单请求 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>
我确定我在这里遗漏了一些东西,我提前为我的英语道歉。任何帮助将不胜感激。
感谢您的评论,这里的建议很有帮助,值得研究。但是,我将在此处介绍的解决方案不会直接回答我的问题,即我的代码如何或为何不产生任何错误消息。所以现在,这是我解决问题的方法:
- 启用 Drive API. 在阅读了关于从服务帐户向 Drive API 发出请求的大部分文章和文档后,很明显如果我们没有从 Google API 控制台启用驱动器 API,我的代码将无法工作。
- 正在将三个依赖的版本降级到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>
- 将 属性
setSupportsTeamDrive
的值设置为 true。 如果没有 属性 我们将无法将文件保存到Team Drive 中的共享文件夹。
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());
}
- 方法拆分,逻辑更清晰
来自 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;
}