在Beam

时间:2019-07-19 14:40:11

标签: google-cloud-dataflow apache-beam

我正在尝试将特定文件从存储桶A复制到存储桶B。存储桶A是结构化的(目录),而存储桶B将没有目录。面临的挑战是,我需要根据文件的原始文件名命名。通常,我会创建一个自定义文件名策略并根据需要对其进行修改。但是,我知道访问原始文件名的唯一方法是通过每个元素并提取其元数据。如何获得对TextIO.write中每个元素的访问权限?

我已经考虑过在TextIO.write之前创建一个转换,该转换接受元素的集合并输出KV的集合,其中键是原始文件名,值是元素(similar to this example)。但是,如果我这样做了,我的作家怎么知道怎么写KV?

通过使用writedynamic并按可序列化函数中每个元素的文件名进行分区,我能够获得这种破解方式。然后,我可以将partitiontype传递给我的文件名策略,然后实现所需的结果。话虽这么说,这似乎远非高效,并且不是为此而设计的,因为我实际上不需要分区任何东西。

3 个答案:

答案 0 :(得分:1)

您可能希望考虑以下几种方法,具体取决于您是尝试一次性复制还是创建某种方式来执行此系统:

如果您只是想复制文件。然后,您可能根本不需要数据流。您可以使用gsutil复制文件。

如果您只需要复制文件而无需修改,而仍然想使用数据流,则可以自己在数据流中使用gsutil。

如果需要转换每个文件。您可能要进行转换,使其对整个文件起作用,将其完全读取并完全修改,然后在自定义ParDo中写出。 Example

或者使用数据流。每当创建GCS文件时,您都可以使用Google cloud functions to trigger

注意:TextIO和FileIO是基于记录的转换,而不是基于文件的转换。他们将文件应用程序放入记录中,以实现并行性。原始文件名和记录顺序实际上并未得到维护。我看到您尝试过使用KV维护文件名,但是正如您提到的,FileIO不允许您将每条记录都传递给文件名。

答案 1 :(得分:0)

使用writeDynamic时,by方法指定用于将传入数据划分到其相应目标的标准。例如,如果这是根据KV对的键决定的,则可以使用.by(KV::getKey),而由于.withNaming可以调整目标文件名。

此外,使用via方法,我们可以提供一个要应用到每个分区的功能,如here所述。在这种情况下,我们想使用键来选择目标,但是我们不想在输出文件中写入它们。因此,我们可以写出值并用.via(Contextful.fn(KV::getValue), TextIO.sink())省略键。

尽管by接受SerializableFunction作为参数,但是via方法要求使用Contextful<Contextful.Fn<UserT,OutputT>> outputFn。这就是为什么我将KV::getValue包装在Contextful.fn()中的原因。在诸如this template之类的示例中,提供上下文(如所需的侧面输入)可能会很有用,但在这里我只想传递该函数。

代码段(更多详细信息here

p.apply("Create Data", Create.of(KV.of("one", "this is row 1"), KV.of("two", "this is row 2"), KV.of("three", "this is row 3"), KV.of("four", "this is row 4")))
 .apply(FileIO.<String, KV<String, String>>writeDynamic()
    .by(KV::getKey)
    .withDestinationCoder(StringUtf8Coder.of())
    .via(Contextful.fn(KV::getValue), TextIO.sink())
    .to(output)
    .withNaming(key -> FileIO.Write.defaultNaming("file-" + key, ".txt")));

答案 2 :(得分:0)

我遇到了java.lang.IllegalArgumentException的错误:当我尝试运行以下代码时,无法反序列化FileBasedSink:

    public static void main(String[] args) {
    WordCountOptions options = PipelineOptionsFactory.fromArgs(args).withValidation().as(WordCountOptions.class);

    Pipeline p = Pipeline.create(options);
    p.apply("Create Data", Create.of(KV.of("one", "this is row 1"), KV.of("two", "this is row 2"), KV.of("three", "this is row 3"), KV.of("four", "this is row 4")))
     .apply(FileIO.<String, KV<String, String>>writeDynamic()
        .by(KV::getKey)
        .withDestinationCoder(StringUtf8Coder.of())
        .via(Contextful.fn(KV::getValue), TextIO.sink())
        .to("a")
        .withNaming(key -> FileIO.Write.defaultNaming("file-" + key, ".txt")));

    p.run().waitUntilFinish();
}

可以请您帮忙吗?

谢谢