如何使用 Java 从 Google Cloud Storage 逐行读取一个巨大的 CSV 文件?
How to read a huge CSV file from Google Cloud Storage line by line using Java?
我是 Google 云平台的新手。我正在尝试逐行读取 Google 云存储(非 public 存储桶通过服务帐户密钥访问)中存在的 CSV 文件,大约 1GB。
我找不到任何选项来逐行读取 Google 云存储 (GCS) 中的文件。我只看到 chunksize/byte 大小选项的阅读。因为我正在尝试读取 CSV,所以我不想使用 read by chunksize 因为它可能会在读取时拆分记录。
目前尝试过的解决方案:
尝试将 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();
请提出方法。
最简单的方法之一可能是使用 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 中,因此一旦它启动 运行,您就可以像访问 GCS 一样引用 GCS 资源文件或 URI。例如:
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...
}
Brandon Yarbrough 是对的,补充一下他的回答:
如果您使用 gcloud 使用您的凭据登录,那么 Brandon 的代码将起作用:google-cloud-nio
将使用您的登录信息来访问文件(即使它们不是 public,这也会起作用) .
如果您更喜欢在软件中完成这一切,您可以使用此代码从本地文件读取凭据,然后从 Google 云端访问您的文件:
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
对象。
由于我正在进行批处理,因此我在 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"));
我是 Google 云平台的新手。我正在尝试逐行读取 Google 云存储(非 public 存储桶通过服务帐户密钥访问)中存在的 CSV 文件,大约 1GB。
我找不到任何选项来逐行读取 Google 云存储 (GCS) 中的文件。我只看到 chunksize/byte 大小选项的阅读。因为我正在尝试读取 CSV,所以我不想使用 read by chunksize 因为它可能会在读取时拆分记录。
目前尝试过的解决方案: 尝试将 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();
请提出方法。
最简单的方法之一可能是使用 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 中,因此一旦它启动 运行,您就可以像访问 GCS 一样引用 GCS 资源文件或 URI。例如:
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...
}
Brandon Yarbrough 是对的,补充一下他的回答:
如果您使用 gcloud 使用您的凭据登录,那么 Brandon 的代码将起作用:google-cloud-nio
将使用您的登录信息来访问文件(即使它们不是 public,这也会起作用) .
如果您更喜欢在软件中完成这一切,您可以使用此代码从本地文件读取凭据,然后从 Google 云端访问您的文件:
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
对象。
由于我正在进行批处理,因此我在 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"));