Перейти к основному содержимому

Взаимодействуйте с Google Sheets из Java

· 8 мин. чтения

1. Обзор

Google Sheets предоставляет удобный способ хранения электронных таблиц и управления ими, а также совместной работы над документом с другими пользователями.

Иногда бывает полезно получить доступ к этим документам из приложения, например, для выполнения автоматизированной операции. Для этой цели Google предоставляет API Google Sheets, с которым разработчики могут взаимодействовать.

В этой статье мы рассмотрим, как мы можем подключиться к API и выполнять операции в Google Таблицах.

2. Зависимости Maven

Чтобы подключиться к API и управлять документами, нам нужно добавить зависимости google-api-client , google-oauth-client-jetty и google-api-services-sheets :

<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.23.0</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-jetty</artifactId>
<version>1.23.0</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-sheets</artifactId>
<version>v4-rev493-1.23.0</version>
</dependency>

3. Авторизация

API Google Sheets требует авторизации OAuth 2.0 , прежде чем мы сможем получить к нему доступ через приложение.

Во-первых, нам нужно получить набор учетных данных OAuth, а затем использовать их в нашем приложении для отправки запроса на авторизацию.

3.1. Получение учетных данных OAuth 2.0

Чтобы получить учетные данные, нам нужно создать проект в Google Developers Console , а затем включить API Google Таблиц для проекта. Первый шаг в руководстве Google Quickstart содержит подробную информацию о том, как это сделать.

После того, как мы загрузили файл JSON с учетными данными, давайте скопируем содержимое файла google-sheets-client-secret.json в каталог src/main/resources нашего приложения.

Содержимое файла должно быть примерно таким:

{
"installed":
{
"client_id":"<your_client_id>",
"project_id":"decisive-octane-187810",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
"client_secret":"<your_client_secret>",
"redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]
}
}

3.2. Получение объекта учетных данных

Успешная авторизация возвращает объект Credential , который мы можем использовать для взаимодействия с API Google Sheets.

Давайте создадим класс GoogleAuthorizeUtil со статическим методом authorize() , который считывает содержимое файла JSON выше и создает объект GoogleClientSecrets .

Затем мы создадим GoogleAuthorizationCodeFlow и отправим запрос на авторизацию:

public class GoogleAuthorizeUtil {
public static Credential authorize() throws IOException, GeneralSecurityException {

// build GoogleClientSecrets from JSON file

List<String> scopes = Arrays.asList(SheetsScopes.SPREADSHEETS);

// build Credential object

return credential;
}
}

В нашем примере мы устанавливаем область SPREADSHEETS , так как мы хотим получить доступ к Google Sheets и использовать DataStoreFactory в памяти для хранения полученных учетных данных. Другой вариант — использовать FileDataStoreFactory для хранения учетных данных в файле.

Полный исходный код класса GoogleAuthorizeUtil см . в проекте GitHub . ``

4. Создание экземпляра службы Sheets

Для взаимодействия с Google Sheets нам понадобится объект Sheets, который является клиентом для чтения и записи через API .

Давайте создадим класс SheetsServiceUtil , который использует указанный выше объект Credential для получения экземпляра Sheets:

public class SheetsServiceUtil {
private static final String APPLICATION_NAME = "Google Sheets Example";

public static Sheets getSheetsService() throws IOException, GeneralSecurityException {
Credential credential = GoogleAuthorizeUtil.authorize();
return new Sheets.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
JacksonFactory.getDefaultInstance(), credential)
.setApplicationName(APPLICATION_NAME)
.build();
}
}

Далее мы рассмотрим некоторые из наиболее распространенных операций, которые мы можем выполнять с помощью API.

5. Запись значений на листе

Для взаимодействия с существующей электронной таблицей необходимо знать идентификатор этой электронной таблицы, который мы можем найти по ее URL-адресу.

Для наших примеров мы будем использовать общедоступную электронную таблицу под названием «Расходы», расположенную по адресу:

https://docs.google.com/spreadsheets/d/1sILuxZUnyl_7-MlNThjt765oWshN3Xs-PPLfqYe4DhI/edit#gid=0

На основе этого URL-адреса мы можем определить идентификатор этой электронной таблицы как «1sILuxZUnyl_7-MlNThjt765oWshN3Xs-PPLfqYe4DhI».

Кроме того, для чтения и записи значений мы будем использовать коллекции электронных таблиц .

Значения представлены в виде объектов ValueRange , которые представляют собой списки списков объектов Java , соответствующих строкам или столбцам на листе.

Давайте создадим тестовый класс, в котором мы инициализируем наш сервисный объект Sheets и константу SPREADSHEET_ID:

public class GoogleSheetsLiveTest {
private static Sheets sheetsService;
private static String SPREADSHEET_ID = // ...

@BeforeClass
public static void setup() throws GeneralSecurityException, IOException {
sheetsService = SheetsServiceUtil.getSheetsService();
}
}

Затем мы можем записать значения:

  • запись в один диапазон
  • запись в несколько диапазонов
  • добавление данных после таблицы

5.1. Запись в один диапазон

Чтобы записать значения в один диапазон на листе, мы будем использовать метод электронных таблиц().values().update() :

@Test
public void whenWriteSheet_thenReadSheetOk() throws IOException {
ValueRange body = new ValueRange()
.setValues(Arrays.asList(
Arrays.asList("Expenses January"),
Arrays.asList("books", "30"),
Arrays.asList("pens", "10"),
Arrays.asList("Expenses February"),
Arrays.asList("clothes", "20"),
Arrays.asList("shoes", "5")));
UpdateValuesResponse result = sheetsService.spreadsheets().values()
.update(SPREADSHEET_ID, "A1", body)
.setValueInputOption("RAW")
.execute();
}

Здесь мы сначала создаем объект ValueRange с несколькими строками, содержащими список расходов за два месяца.

Затем мы используем метод update() для создания запроса, который записывает значения в электронную таблицу с заданным идентификатором, начиная с ячейки «A1».

Чтобы отправить запрос, мы используем метод execute() .

Если мы хотим, чтобы наши наборы значений рассматривались как столбцы, а не строки, мы можем использовать метод setMajorDimension("COLUMNS") .

Параметр ввода «RAW» означает, что значения записываются точно так, как они есть, а не вычисляются.

При выполнении этого теста JUnit приложение откроет окно браузера, используя системный браузер по умолчанию, который попросит пользователя войти в систему и предоставить нашему приложению разрешение на взаимодействие с Google Sheets от имени пользователя:

./4a3cbe19ddbbc13654176ec7c4e43199.png

Обратите внимание, что этот ручной шаг можно пропустить, если у вас есть сервисный аккаунт OAuth .

Требование для того, чтобы приложение могло просматривать или редактировать электронную таблицу, заключается в том, что вошедший в систему пользователь имеет доступ к просмотру или редактированию. В противном случае запрос приведет к ошибке 403. Электронная таблица, которую мы используем для нашего примера, настроена на общедоступный доступ для редактирования.

Теперь, если мы проверим электронную таблицу, мы увидим, что диапазон « A1:B6 » обновлен нашими наборами значений.

Давайте перейдем к записи в несколько разрозненных диапазонов в одном запросе.

5.2. Запись в несколько диапазонов

Если мы хотим обновить несколько диапазонов на листе, мы можем использовать BatchUpdateValuesRequest для повышения производительности:

List<ValueRange> data = new ArrayList<>();
data.add(new ValueRange()
.setRange("D1")
.setValues(Arrays.asList(
Arrays.asList("January Total", "=B2+B3"))));
data.add(new ValueRange()
.setRange("D4")
.setValues(Arrays.asList(
Arrays.asList("February Total", "=B5+B6"))));

BatchUpdateValuesRequest batchBody = new BatchUpdateValuesRequest()
.setValueInputOption("USER_ENTERED")
.setData(data);

BatchUpdateValuesResponse batchResult = sheetsService.spreadsheets().values()
.batchUpdate(SPREADSHEET_ID, batchBody)
.execute();

В этом примере мы сначала создаем список ValueRanges, каждый из которых состоит из двух ячеек, представляющих название месяца и общие расходы.

Затем мы создаем BatchUpdateValuesRequest с опцией ввода «USER_ENTERED», а не «RAW», что означает, что значения ячеек будут вычисляться на основе формулы добавления двух других ячеек.

Наконец, мы создаем и отправляем запрос batchUpdate . В результате диапазоны « D1:E1 » и « D4:E4 » будут обновлены.

5.3. Добавление данных после таблицы

Другой способ записи значений на листе — добавление их в конец таблицы.

Для этого мы можем использовать метод append() :

ValueRange appendBody = new ValueRange()
.setValues(Arrays.asList(
Arrays.asList("Total", "=E1+E4")));
AppendValuesResponse appendResult = sheetsService.spreadsheets().values()
.append(SPREADSHEET_ID, "A1", appendBody)
.setValueInputOption("USER_ENTERED")
.setInsertDataOption("INSERT_ROWS")
.setIncludeValuesInResponse(true)
.execute();

ValueRange total = appendResult.getUpdates().getUpdatedData();
assertThat(total.getValues().get(0).get(1)).isEqualTo("65");

Во-первых, мы создаем объект ValueRange , содержащий значения ячеек, которые мы хотим добавить.

В нашем случае это содержит ячейку с общими расходами за оба месяца, которые мы находим, складывая значения ячеек «E1» и «E2» .

Затем мы создаем запрос, который добавит данные после таблицы, содержащей ячейку « A1 ».

Параметр INSERT_ROWS означает, что мы хотим, чтобы данные добавлялись в новую строку, а не заменяли какие-либо существующие данные после таблицы. Это означает, что пример запишет диапазон « A7:B7 » при первом запуске.

При последующих запусках таблица, которая начинается с ячейки «A1» , теперь будет растягиваться, чтобы включить строку «A7: B7» , поэтому новая строка переходит к строке «A8: B8» и так далее.

Нам также необходимо установить для свойства includeValuesInResponse значение true, если мы хотим проверить ответ на запрос . В результате объект ответа будет содержать обновленные данные.

6. Чтение значений с листа

Давайте проверим правильность написания наших значений, прочитав их с листа.

Мы можем сделать это, используя метод spreadsheets().values().get() для чтения одного диапазона или метод batchUpdate() для чтения нескольких диапазонов:

List<String> ranges = Arrays.asList("E1","E4");
BatchGetValuesResponse readResult = sheetsService.spreadsheets().values()
.batchGet(SPREADSHEET_ID)
.setRanges(ranges)
.execute();

ValueRange januaryTotal = readResult.getValueRanges().get(0);
assertThat(januaryTotal.getValues().get(0).get(0))
.isEqualTo("40");

ValueRange febTotal = readResult.getValueRanges().get(1);
assertThat(febTotal.getValues().get(0).get(0))
.isEqualTo("25");

Здесь мы читаем диапазоны «E1» и «E4» и проверяем, содержат ли они итоговую сумму за каждый месяц, которую мы записали ранее.

7. Создание новых электронных таблиц

Помимо чтения и обновления значений, мы также можем манипулировать листами или целыми электронными таблицами, используя коллекции электронных таблиц() и электронных таблиц().sheets() .

Давайте посмотрим на пример создания новой электронной таблицы:

@Test
public void test() throws IOException {
Spreadsheet spreadSheet = new Spreadsheet().setProperties(
new SpreadsheetProperties().setTitle("My Spreadsheet"));
Spreadsheet result = sheetsService
.spreadsheets()
.create(spreadSheet).execute();

assertThat(result.getSpreadsheetId()).isNotNull();
}

Здесь мы сначала создаем объект электронной таблицы с заголовком « Моя таблица» , затем строим и отправляем запрос с использованием методов create() и execute() .

Новая таблица будет закрыта и размещена на Диске пользователя, вошедшего в систему.

8. Другие операции обновления

Большинство других операций принимают форму объекта Request , который затем добавляется в список и используется для создания BatchUpdateSpreadsheetRequest.

Давайте посмотрим, как мы можем отправить два запроса на изменение заголовка электронной таблицы и скопировать-вставить набор ячеек с одного листа на другой:

@Test
public void whenUpdateSpreadSheetTitle_thenOk() throws IOException {
UpdateSpreadsheetPropertiesRequest updateSpreadSheetRequest
= new UpdateSpreadsheetPropertiesRequest().setFields("*")
.setProperties(new SpreadsheetProperties().setTitle("Expenses"));

CopyPasteRequest copyRequest = new CopyPasteRequest()
.setSource(new GridRange().setSheetId(0)
.setStartColumnIndex(0).setEndColumnIndex(2)
.setStartRowIndex(0).setEndRowIndex(1))
.setDestination(new GridRange().setSheetId(1)
.setStartColumnIndex(0).setEndColumnIndex(2)
.setStartRowIndex(0).setEndRowIndex(1))
.setPasteType("PASTE_VALUES");

List<Request> requests = new ArrayList<>();
requests.add(new Request()
.setCopyPaste(copyRequest));
requests.add(new Request()
.setUpdateSpreadsheetProperties(updateSpreadSheetRequest));

BatchUpdateSpreadsheetRequest body
= new BatchUpdateSpreadsheetRequest().setRequests(requests);

sheetsService.spreadsheets().batchUpdate(SPREADSHEET_ID, body).execute();
}

Здесь мы создаем объект UpdateSpreadSheetPropertiesRequest , который указывает новый заголовок, объект CopyPasteRequest , который содержит источник и место назначения операции, а затем добавляем эти объекты в список запросов .

Затем мы выполняем оба запроса как пакетное обновление.

Многие другие типы запросов доступны для использования аналогичным образом. Например, мы можем создать новый лист в электронной таблице с помощью AddSheetRequest или изменить значения с помощью FindReplaceRequest.

Мы можем выполнять другие операции, такие как изменение границ, добавление фильтров или объединение ячеек. Полный список типов запросов доступен здесь .

9. Заключение

В этой статье мы увидели, как мы можем подключиться к API Google Sheets из приложения Java, и несколько примеров манипулирования документами, хранящимися в Google Sheets.

Полный исходный код примеров можно найти на GitHub .