0
Help us understand the problem. What are the problem?

posted at

updated at

SheetAPI v4とDriveAPIを使ってJavaでスプレッドシートを操作する

Javaのwebアプリからスプレッドシートを操作したいという要望があったのだが、SheetAPIのv3については記事がいっぱいあるのだけどv4については少なくて使い方を試すのに苦労したので備忘録として書いておく。

1. 事前準備

1.1. サービスアカウントの作成

サービスアカウントの認証情報を持ったjsonファイルを作成。
作成の手順はあちらこちらのネット上にあるが、例えば以下を参照
※SheetAPIだけでなくDriveAPIも有効にする必要あり
サービスアカウントで認証してGoogleSpreadsheetからデータを取得

1.2. ユーザ側の作業

ユーザは、アプリからのアクセスを許可するスプレッドシートやドライブのフォルダに対し、サービスアカウント作成時に生成されたメールアドレスを共有設定に追加する。

2. 環境構築

今回はJavaのSpringBootプロジェクトでWEBアプリを作る想定で構築する。
依存性についてはMavenを使用。

2.1. バージョン

Javaの開発環境としての条件配下の通り。

  • Java1.8以降
  • Gradle2.3以降
  • Mavenでも可(前提バージョンは確認できず)

2.2. 認証情報の配置

サービスアカウント作成時に生成したjsonファイルを、Mavenプロジェクトの src/main/resources/ に配置する。
API使用時に読み込んでいる必要がある。

2.3. 依存性の設定

以下のライブラリを読み込む。

  • google-api-client
  • google-oauth-client-jetty
  • google-auth-library-oauth2-http
  • google-api-services-sheets
  • google-api-services-drive

pom.xmlに記載する内容(バージョンは要確認)

<dependency>
    <groupId>com.google.api-client</groupId>
    <artifactId>google-api-client</artifactId>
    <version>1.33.1</version>
</dependency>
<dependency>
    <groupId>com.google.oauth-client</groupId>
    <artifactId>google-oauth-client-jetty</artifactId>
    <version>1.32.1</version>
</dependency>
<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-sheets</artifactId>
    <version>v4-rev612-1.25.0</version>
</dependency>
<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-drive</artifactId>
    <version>v3-rev197-1.25.0</version>
</dependency>
<dependency>
    <groupId>com.google.auth</groupId>
    <artifactId>google-auth-library-oauth2-http</artifactId>
    <version>1.6.0</version>
</dependency>

3. コーディング

分かりやすくするために、認証情報を取得する部分、実際の処理を行う部分のみ以下に記載する。

スプレッドシートの編集については下記のサイトを参照した。
JavaからGoogleSpreadsheetに書く その2(Java側)

3.1. 認証情報を取得する

サービスアカウントの認証情報を持つjsonファイルを読み込んで、クレデンシャルのオブジェクトを返却する。

public class AuthUtil {
	public static final List<String> SCOPES = Arrays.asList(DriveScopes.DRIVE, SheetsScopes.SPREADSHEETS);
	public static final String CREDENTIALS_FILE_PATH = "/JSONファイル名";
	public static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();

    public static HttpRequestInitializer getCredentials() throws IOException {
    	
    	GoogleCredentials credential 
    		= ServiceAccountCredentials.fromStream(AuthUtil.class.getResourceAsStream(CREDENTIALS_FILE_PATH)).createScoped(SCOPES);
    	
    	HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credential);
    	
    	return requestInitializer;
    }
}

SCOPESに指定している文字列で、APIで操作する範囲を指定する。この例ではGoogleドライブとスプレッドシートを操作する。

なお、古いバージョンでは以下の方法でクレデンシャルを返す例が見られるが、GoogleCredentialは非推奨のクラスとなっている。

GoogleCredential credential = GoogleCredential.fromStream(AuthUtil.class.getResourceAsStream(CREDENTIALS_FILE_PATH)).createScoped(SCOPES);
return credential;

3.2. 実際の処理を行う

ここからはスプレッドシートとGoogleドライブを実際に操作する例を、単純な処理で記載する。

(1) スプレッドシートのセルの値を取得

final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

Sheets service = new Sheets.Builder(HTTP_TRANSPORT, AuthUtil.JSON_FACTORY, AuthUtil.getCredentials())
        .setApplicationName("/** 任意の文字列 **/")
        .build();

final String range = "シート1!A:B";

List<List<Object>> values = service.spreadsheets().values().get(/** スプレッドシートID **/, range).execute().getValues();
for (List row : values) {
    System.out.printf("%s, %s\n", row.get(0), row.get(1));
}
  • get()メソッドでスプレッドシートを指定して取得
  • rangeに値を取得するシート名とセルを指定

(2) スプレッドシートのセルに出力

final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

Sheets service = new Sheets.Builder(HTTP_TRANSPORT, AuthUtil.JSON_FACTORY, AuthUtil.getCredentials())
        .setApplicationName("/** 任意の文字列 **/"))
        .build();

Object[][] values = { { 1, "A" }, { 2.1D, "B" }, { 2.50E-3, "C" }, { "2022/04/01", "D" } };
List<List<Object>> valueList = new ArrayList<>();
for (Object[] row : values) {
    valueList.add(Arrays.asList(row));
}

ValueRange valueRange = new ValueRange().setValues(valueList).setMajorDimension("ROWS");
String range = "シート1" + "!" + bijectiveBase26(1) + 1;
service.spreadsheets().values().update("/** スプレッドシートID **/", range, valueRange).setValueInputOption("USER_ENTERED").execute();
  • Sheets オブジェクトを取得する方法はお約束の書き方となる。
  • Object[][] の配列に出力するデータを代入する。
  • setMajorDimension()にはROWS(行)COLUMNS(列)を設定(大概は行単位に処理していくのでROWSでよいかと)
  • range の文字列に対象となるシート名と開始セル位置を代入する。
  • update() メソッドに必要な情報を渡してexcute で実行する。
  • setValueInputOption()にはUSER_ENTEREDRAWSのどちらかを設定
    bijectiveBase26()はセルの列を取得するユーティリティメソッド
// 列番号を列番号文字列に変換する
private String bijectiveBase26(int n) {
    StringBuilder buf = new StringBuilder();
    while (n != 0) {
        buf.append((char) ((n - 1) % 26 + 'A'));
        n = (n - 1) / 26;
    }
    return buf.reverse().toString();
}

(3) スプレッドシートの行追加

service.spreadsheets().values().append(spreadsheetId, "シート1" + "!" + bijectiveBase26(1) + 1001, valueRange)
    .setValueInputOption("USER_ENTERED")
    .execute();
  • appendで行追加を行う

(4) スプレッドシートに新規シートを追加する

final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

Sheets service = new Sheets.Builder(HTTP_TRANSPORT, AuthUtil.JSON_FACTORY, AuthUtil.getCredentials())
        .setApplicationName("/** 任意の文字列 **/")
        .build();

SheetProperties sheetProperties = new SheetProperties().setTitle("追加シート");
AddSheetRequest addSheetRequest = new AddSheetRequest().setProperties(sheetProperties);
List<Request> requests = new ArrayList<>();
requests.add(new Request().setAddSheet(addSheetRequest));
service.spreadsheets().batchUpdate("/** スプレッドシートID **/", new BatchUpdateSpreadsheetRequest().setRequests(requests)).execute();
  • batchUpdate()execute()を実行する
  • 引数のRequestに追加するシートのオブジェクトをセットする

(5) スプレッドシートを取得する

Spreadsheet spreadsheet = service.spreadsheets().get(spreadsheetId).setIncludeGridData(false).execute();
int sheetCount = spreadsheet.getSheets().size();
  • spreadsheetオブジェクトにあるgetSheets()にて、スプレッドシート内のシートを取得できる

(6) ドライブのフォルダにスプレッドシートを作成する

final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Drive driveService = new Drive.Builder(HTTP_TRANSPORT, AuthUtil.JSON_FACTORY, AuthUtil.getCredentials())
	.setApplicationName("/** 任意の文字列 **/")
	.build();

File file = new File().setName("新スプレッドシート")
	.setMimeType("application/vnd.google-apps.spreadsheet")
	.setParents(Arrays.asList("/** GoogleドライブのフォルダID **/"));
driveService.files().create(file).execute();
  • スプレッドシート自体の新規作成はDriveAPIで行う
  • setMimeType()に追加するファイルの種別を設定
  • DriveAPIで使用できるMimetypeについてはGoogolのガイドを参照
  • parentsはフォルダを表す

(7) ドライブのフォルダにあるスプレッドシート一覧を取得する

final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

Drive driveService = new Drive.Builder(HTTP_TRANSPORT, AuthUtil.JSON_FACTORY, AuthUtil.getCredentials())
		.setApplicationName("/** 任意の文字列 **/")
		.build();

String query = String.format("'%s' in parents and mimeType = '%s' and trashed = false", 
		"/** GoogleドライブのフォルダID **/", "application/vnd.google-apps.spreadsheet");
FileList result = driveService.files().list().setQ(query).execute();
for (File file : result.getFiles()) {
    System.out.printf("Found file: %s (%s)\n",file.getName(), file.getId());
}

(8) ドライブのフォルダにあるスプレッドシートをコピーする

final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

Drive driveService = new Drive.Builder(HTTP_TRANSPORT, AuthUtil.JSON_FACTORY, AuthUtil.getCredentials())
		.setApplicationName("/** 任意の文字列 **/")
		.build();

String query = String.format("'%s' in parents and mimeType = '%s' and trashed = false", 
		"/** GoogleドライブのフォルダID **/", "application/vnd.google-apps.spreadsheet");
FileList result = driveService.files().list().setQ(query).execute();
for (File file : result.getFiles()) {
	File metadata = new File().setName(file.getName() + "複製")
    		.setMimeType("application/vnd.google-apps.spreadsheet")
    		.setParents(Arrays.asList("/** GoogleドライブのフォルダID **/"));

    driveService.files().copy(file.getId(), metadata).execute();
}
  • copy()の引数にコピーしたいスプレッドシートのIDとコピー後のスプレッドシートの情報を指定してexecuteを実行する

(9) スプレッドシートIDからフォルダIDを取得する

Drive driveService = new Drive.Builder(HTTP_TRANSPORT, AuthUtil.JSON_FACTORY, AuthUtil.getCredentials())
		.setApplicationName("/** 任意の文字列 **/")
		.build();

File file = driveService.files().get("/** スプレッドシートID **/").setFields("id, name, parents").execute();
System.out.println("Parents: " + file.getParents());
  • setFields()で検索時に取得する項目を指定
  • フォルダを示す項目名はParents

以上

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?