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

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

1. обзор

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

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

В этой статьеwe’re going to take a look at how we can connect to the API and perform operations on Google Sheets.

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

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


    com.google.api-client
    google-api-client
    1.23.0


    com.google.oauth-client
    google-oauth-client-jetty
    1.23.0


    com.google.apis
    google-api-services-sheets
    v4-rev493-1.23.0

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

The Google Sheets API requires OAuth 2.0 authorization, прежде чем мы сможем получить к нему доступ через приложение.

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

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

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

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

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

{
  "installed":
    {
      "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":"",
      "redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]
    }
}

3.2. Получение объектаCredential

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

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

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

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

        // build GoogleClientSecrets from JSON file

        List scopes = Arrays.asList(SheetsScopes.SPREADSHEETS);

        // build Credential object

        return credential;
    }
}

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

Полный исходный кодGoogleAuthorizeUtil class, можно найти вthe GitHub project.

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

Для взаимодействия с Google Таблицами нам понадобитсяa Sheets object which is the client for reading and writing through the 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.

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

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

Такжеto read and write values, we’re going to use spreadsheets.values collections.

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

Давайте создадим тестовый класс, в котором мы инициализируем объект службы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. Запись в один диапазон

Чтобы записать значения в один диапазон на листе, мы будем использовать методspreadsheets().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» означает, что значения записываются в точности так, как они есть, а не вычисляются.

При выполнении этого теста JUnitthe application will open a browser window using the system’s default browser that asks the user to log in and give our application permission to interact with Google Sheets on the user’s behalf:

image

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

A requirement for the application to be able to view or edit the spreadsheet is that the signed-in user has a view or edit access to it. В противном случае запрос приведет к ошибке 403. Электронная таблица, которую мы используем в нашем примере, настроена на публичное редактирование.

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

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

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

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

List 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 сthe input option “USER_ENTERED”, as opposed to “RAW”, meaning the cell values will be computed based on the formula добавления двух других ячеек.

Наконец, мы создаем и отправляем запрос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 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. Создание новых таблиц

Помимо чтения и обновления значений, мы также можем управлять листами или целыми электронными таблицами, используя коллекцииspreadsheets() иspreadsheets().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();
}

Здесь мы сначала создаем объектSpreadsheet с заголовком «MySpreadsheet”, затем создаем и отправляем запрос с помощью методов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 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, содержащий источник и назначение операции, а затем добавляем эти объекты вList изRequests.с

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

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

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

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

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

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