【问题标题】:Java to Google SpreadsheetJava 到 Google 电子表格
【发布时间】:2015-07-20 02:14:09
【问题描述】:

我正在尝试使用 Java 进行编程以连接到 Google 电子表格以进行数据检索或修改单元格中的数据。

我的 Google 电子表格链接是 https://docs.google.com/spreadsheets/d/1UXoGD2gowxZ2TY3gooI9y7rwWTPBOA0dnkeNYwUqQRA

我查看了Sheets API,它需要像

这样的链接

https://spreadsheets.google.com/feeds/worksheets/key/private/full

我尝试了不同形式的链接,例如:

  1. https://spreadsheets.google.com/feeds/worksheets/1UXoGD2gowxZ2TY3gooI9y7rwWTPBOA0dnkeNYwUqQRA/private/full

  2. https://spreadsheets.google.com/feeds/worksheets/1UXoGD2gowxZ2TY3gooI9y7rwWTPBOA0dnkeNYwUqQRA/private/full

他们分别给了我不同类型的错误:

  1. com.google.gdata.util.ParseException: Unrecognized content type:application/binary
  2. com.google.gdata.util.RedirectRequiredException: Moved Temporarily

我不知道如何使用 Java 连接到 Googl 电子表格。如果您有这方面的经验,请帮助我。

import com.google.gdata.client.authn.oauth.*;
import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.*;
import com.google.gdata.data.batch.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;
import org.testng.annotations.Test;

import java.io.IOException;
import java.net.*;
import java.util.*;

public class TestGoogleSheetsAPI {

    @Test
    public void testConnectToSpreadSheet() throws ServiceException, IOException {
        SpreadsheetService service = new SpreadsheetService("google-spreadsheet");

        URL SPREADSHEET_FEED_URL = new URL("https://spreadsheets.google.com/feeds/worksheets/1UXoGD2gowxZ2TY3gooI9y7rwWTPBOA0dnkeNYwUqQRA/public/full");
        SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
        List<SpreadsheetEntry> spreadsheets = feed.getEntries();

        if (spreadsheets.size() == 0) {
            // TODO: There were no spreadsheets, act accordingly.
        }

        SpreadsheetEntry spreadsheet = spreadsheets.get(0);
        System.out.println(spreadsheet.getTitle().getPlainText());
    }
}

我没有使用service.setUserCredentials("xxx@gmail", "password"),因为我会有另一个错误,即com.google.gdata.util.AuthenticationException: Error authenticating (check service name)

【问题讨论】:

  • 您是否设置了 Java 客户端库?还是您使用的是原始 REST HTTPS 请求?
  • 我使用maven导入gdata库。我真的应该下载 JAR 文件吗?
  • 我不知道。我只是想确保你已经完成了基础知识。请显示发出请求的 Java 代码。
  • 添加了上面的代码
  • 我认为您需要使用 oauth 进行身份验证。网址https://spreadsheets.google.com/feeds 属于oauth 信息的范围。在这里查看更多developers.google.com/google-apps/spreadsheets

标签: java google-sheets google-spreadsheet-api


【解决方案1】:

您收到重定向是因为访问您的电子表格需要您先进行身份验证。 Google 表格使用旧的 gdata API,但要求您使用 OAuth 2.0 进行身份验证。因此,您需要同时导入 gdata 和 Google API 库,如下所示:

<dependencies>
    <dependency>
        <groupId>com.google.gdata</groupId>
        <artifactId>core</artifactId>
        <version>1.47.1</version>
    </dependency>
    <dependency>
        <groupId>com.google.api-client</groupId>
        <artifactId>google-api-client-java6</artifactId>
        <version>1.20.0</version>
    </dependency>
</dependencies>

以下代码显示了如何使用 OAuth 向 Google 进行身份验证。您需要follow the instructions for creating a service account and downloading the P12 key first. 创建您的服务帐户后,将电子邮件地址复制到下面的 CLIENT_ID 字段中,将您的 P12 文件添加到您的类路径并唱 P12FILE 以指向您的 P12 文件。

我能够使用以下 SPREADSHEET_FEED_URL "https://spreadsheets.google.com/feeds/worksheets/:worksheetId/private/basic" 来解决这个问题,其中 ":worksheetId" 是您的工作表 ID。这与您使用的略有不同。

请务必先与服务帐户电子邮件地址共享电子表格,以确保您的服务帐户有权读取或写入电子表格。

public class GoogleSheetsApiTest {

// Generate a service account and P12 key:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount
private final String CLIENT_ID = "<your service account email address>";
// Add requested scopes.
private final List<String> SCOPES = Arrays
        .asList("https://spreadsheets.google.com/feeds");
// The name of the p12 file you created when obtaining the service account
private final String P12FILE = "/<your p12 file name>.p12";


@Test
public void testConnectToSpreadSheet() throws GeneralSecurityException,
        IOException, ServiceException, URISyntaxException {

    SpreadsheetService service = new SpreadsheetService(
            "google-spreadsheet");
    GoogleCredential credential = getCredentials();
    service.setOAuth2Credentials(credential);

    URL SPREADSHEET_FEED_URL = new URL(
            "https://spreadsheets.google.com/feeds/worksheets/1UXoGD2gowxZ2TY3gooI9y7rwWTPBOA0dnkeNYwUqQRA/private/basic");
    SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL,
            SpreadsheetFeed.class);
    List<SpreadsheetEntry> spreadsheets = feed.getEntries();

    if (spreadsheets.size() == 0) {
        // // TODO: There were no spreadsheets, act accordingly.
    }
    //
    SpreadsheetEntry spreadsheet = spreadsheets.get(0);
    System.out.println(spreadsheet.getTitle().getPlainText());

}

private GoogleCredential getCredentials() throws GeneralSecurityException,
        IOException, URISyntaxException {
    JacksonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    HttpTransport httpTransport = GoogleNetHttpTransport
            .newTrustedTransport();

    URL fileUrl = this.getClass().getResource(P12FILE);
    GoogleCredential credential = new GoogleCredential.Builder()
            .setTransport(httpTransport)
            .setJsonFactory(JSON_FACTORY)
            .setServiceAccountId(CLIENT_ID)
            .setServiceAccountPrivateKeyFromP12File(
                    new File(fileUrl.toURI()))
            .setServiceAccountScopes(SCOPES).build();

    return credential;
}

}

【讨论】:

  • 嗨,布赖恩,非常感谢。我通过 Google 开发者控制台创建了一个服务帐户。 CLIENT_ID 应该是开发者控制台中较长的那个,还是我的真实电子邮件?
  • 我的代码现在运行良好,在.setServiceAccountScopes(SCOPES).build(); 之后添加了credential.refreshToken();。非常感谢您的帮助,布赖恩!
  • @TimLai 如果答案有效,请接受。其他有相同问题的人会知道要寻找哪个答案。
【解决方案2】:

我也收到了com.google.gdata.util.ParseException: Unrecognized content type:application/binary 错误,但我似乎偶然发现了解决这个奇怪错误的方法。 Alex R's answer中的代码可以作为起点。

  1. 首先我尝试将可见性更改为"public",只是想看看会发生什么。由于文档没有发布,我得到了预期的错误响应,包含:

    我们很抱歉。此文档未发布。

  2. 所以我将可见性改回"private",并再次尝试只是为了好玩......

    瞧,瞧!成功了!

它适用于 Drive 或 Spreadsheets Feed 范围(如果您愿意,可以同时包含两者):

我不确定这是否是一个因素,但我没有使用 OAuth 2.0,而是使用从 PrivateKey(PKCS12 文件)创建的 Credential,如 Brian Chapman's answer

结论

因此,虽然它很笨拙,但我已将我的应用程序修改为始终发出"public" 请求(包装在try 块中以吸收生成的Exception),然后再进行"private" 请求。这是我能够确定地得到正确结果的唯一方法。

【讨论】:

    【解决方案3】:

    您从该 URL 获得的提要是 WorksheetFeed,而不是 SpreadsheetFeed。 试试这个:

        SpreadsheetService service = new SpreadsheetService("google-spreadsheet");
    
        FeedURLFactory urlFactory = FeedURLFactory.getDefault();
        WorksheetFeed worksheetFeed = service.getFeed(urlFactory.getWorksheetFeedUrl("1V4jT4vSqmY4YNY1VJhariLRLbxfFWf5z8bSTpDcSBPE", "public", "full"), WorksheetFeed.class);
        List<WorksheetEntry> worksheets = worksheetFeed.getEntries();
        WorksheetEntry worksheet = worksheets.get(0);
        System.out.println(worksheet.getTitle().getPlainText());
    

    【讨论】:

      【解决方案4】:

      我觉得这是一个错误。作为一种解决方法,您可以先在 getfeed 请求下方调用,然后您的 feed 请求将开始工作。以下是修改后的代码以便更好地理解

      import com.google.gdata.client.authn.oauth.*;
      import com.google.gdata.client.spreadsheet.*;
      
      import com.google.gdata.data.*;
      import com.google.gdata.data.batch.*;
      import com.google.gdata.data.spreadsheet.*;
      import com.google.gdata.util.*;
      import org.testng.annotations.Test;
      
      import java.io.IOException;
      import java.net.*;
      import java.util.*;
      
      public class TestGoogleSheetsAPI {
      
          @Test
          public void testConnectToSpreadSheet() throws ServiceException, IOException {
              SpreadsheetService service = new SpreadsheetService("google-spreadsheet");
      
              URL SPREADSHEET_FEED_URL = new URL("https://spreadsheets.google.com/feeds/worksheets/1UXoGD2gowxZ2TY3gooI9y7rwWTPBOA0dnkeNYwUqQRA/public/full");
              //added new line *******************************
              service.getFeed(new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full?xoauth_requestor_id=test"),WorksheetFeed.class);
              //************************
              SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
              List<SpreadsheetEntry> spreadsheets = feed.getEntries();
      
              if (spreadsheets.size() == 0) {
                  // TODO: There were no spreadsheets, act accordingly.
              }
      
              SpreadsheetEntry spreadsheet = spreadsheets.get(0);
              System.out.println(spreadsheet.getTitle().getPlainText());
          }
      }
      

      也做了一些调试。似乎如果我们尝试使用密钥调用工作表,那么响应似乎具有内容类型“应用程序/二进制”,这将在 gdata 库中被 regected,但是如果我们尝试首先获取所有提要,那么响应将具有正确的内容类型和该类型用于下一个请求。所以流程开始工作

      【讨论】:

        【解决方案5】:

        获得 com.google.gdata.util.RedirectRequiredException: Moved Temporarily 的原因之一是我们忘记发布文档/电子表格。通过转到菜单 > 文件 > 发布到 Web,确保在创建文档后已发布文档。这仅需要一次,而不是在每次编辑之后。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-19
          • 1970-01-01
          • 1970-01-01
          • 2015-08-09
          • 2014-09-11
          • 1970-01-01
          相关资源
          最近更新 更多