【问题标题】:How to read a huge CSV file from Google Cloud Storage line by line using Java?如何使用 Java 逐行从 Google Cloud Storage 读取巨大的 CSV 文件?
【发布时间】:2019-03-18 15:55:28
【问题描述】:

我是 Google Cloud Platform 的新手。我正在尝试逐行读取 Google Cloud Storage(通过服务帐户密钥访问的非公共存储桶)中存在的 CSV 文件,大小约为 1GB。

我找不到任何选项来逐行读取 Google Cloud Storage (GCS) 中的文件。我只看到按块大小/字节大小选项读取。由于我正在尝试读取 CSV,因此我不想使用按块大小读取,因为它可能会在读取时拆分记录。

目前尝试的解决方案: 尝试将 GCS 中存在的 CSV 文件中的内容复制到临时本地文件并使用以下代码读取临时文件。下面的代码按预期工作,但我不想将大文件复制到我的本地实例。相反,我想从 GCS 中逐行读取。

    StorageOptions options = 
    StorageOptions.newBuilder().setProjectId(GCP_PROJECT_ID)
            .setCredentials(gcsConfig.getCredentials()).build();
    Storage storage = options.getService();
    Blob blob = storage.get(BUCKET_NAME, FILE_NAME);
    ReadChannel readChannel = blob.reader();
    FileOutputStream fileOuputStream = new FileOutputStream(TEMP_FILE_NAME);
    fileOuputStream.getChannel().transferFrom(readChannel, 0, Long.MAX_VALUE);
    fileOuputStream.close();

请提出方法。

【问题讨论】:

  • ReadChannel 是 ReadableByteChannel 的子类,您可以将其包装在 Reader 中,如 dup 所示。
  • @DougStevenson 我正在批处理中读取文件。我的文件将有 1000 万条记录,我不想一次读取所有行并让我的 JVM 崩溃
  • 我明白了。使用副本中的建议。您可以安排从 BufferedReader 单独接收行。您不必一次全部处理。
  • A FileChannel 和 ReadChannel 共享一个公共超类 ReadableByteChannel,这是您正在寻找的 API。它应该以同样的方式工作。

标签: google-cloud-platform google-cloud-storage nio


【解决方案1】:

由于我正在进行批处理,我在我的 ItemReader 的 init() 方法中使用以下代码,该方法使用 @PostConstruct 进行了注释。在我的 ItemReader 的 read() 中,我正在构建一个列表。列表大小与块大小相同。通过这种方式,我可以根据我的 chunkSize 读取行,而不是一次读取所有行。

StorageOptions options = 
StorageOptions.newBuilder().setProjectId(GCP_PROJECT_ID)
        .setCredentials(gcsConfig.getCredentials()).build();
Storage storage = options.getService();
Blob blob = storage.get(BUCKET_NAME, FILE_NAME);
ReadChannel readChannel = blob.reader();
BufferedReader br = new BufferedReader(Channels.newReader(readChannel, "UTF-8"));

【讨论】:

    【解决方案2】:

    最简单的方法之一可能是使用 google-cloud-nio 包,它是您已经在使用的 google-cloud-java 库的一部分:https://github.com/googleapis/google-cloud-java/tree/v0.30.0/google-cloud-contrib/google-cloud-nio

    它将 Google Cloud Storage 整合到 Java 的 NIO 中,因此一旦启动并运行,您就可以像访问文件或 URI 一样引用 GCS 资源。例如:

    Path path = Paths.get(URI.create("gs://bucket/lolcat.csv"));
    try (Stream<String> lines = Files.lines(path)) {
       lines.forEach(s -> System.out.println(s));
    } catch (IOException ex) {
       // do something or re-throw...
    }
    

    【讨论】:

    • 我看到了这种方法,但我的 GCS 存储桶不是公开的,可以通过服务帐户密钥访问。如果我使用上述方法,我会得到 401 Unauthorized 异常。能否请您提出替代方案?
    • 感谢你们的快速回复。我不想一次读完所有的行。我的文件非常庞大,有 900 万条记录。
    • Files.readAllLines 一次读取所有行。 Files.lines(),在我的示例中,改为通过文件流式传输。
    【解决方案3】:

    Brandon Yarbrough 是对的,补充一下他的回答:

    如果您使用 gcloud 使用您的凭据登录,那么 Brandon 的代码将起作用:google-cloud-nio 将使用您的登录名来访问文件(即使它们不公开也可以使用)。

    如果您更喜欢在软件中完成这一切,您可以使用此代码从本地文件中读取凭据,然后从 Google Cloud 访问您的文件:

        String myCredentials = "/path/to/my/key.json";
        CloudStorageFileSystem fs =
            CloudStorageFileSystem.forBucket(
                "bucket",
                CloudStorageConfiguration.DEFAULT,
                StorageOptions.newBuilder()
                    .setCredentials(ServiceAccountCredentials.fromStream(
                        new FileInputStream(myCredentials)))
                    .build());
        Path path = fs.getPath("/lolcat.csv");
        List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
    

    编辑:你不想一次读完所有的行,所以不要使用realAllLines,但是一旦你有了Path,你就可以使用上面讨论的任何其他技术来读取部分您需要的文件:您可以一次读取一行或获取Channel 对象。

    【讨论】:

      猜你喜欢
      • 2019-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-22
      • 2019-11-17
      • 1970-01-01
      • 2015-04-09
      • 2020-03-07
      相关资源
      最近更新 更多