我想写一个gs文件,但我不知道编译时的文件名。它的名称基于在运行时定义的行为。我该怎么办?
答案 0 :(得分:6)
如果你正在使用Beam Java,你可以使用FileIO.writeDynamic()
(从目前正在发布的Beam 2.3开始 - 但你已经可以通过版本2.3.0-SNAPSHOT
使用它了),或较旧的DynamicDestinations
API(在Beam 2.2中可用)。
根据交易类型,使用FileIO.writeDynamic()
将PCollection
银行交易写入GCS上不同路径的示例:
PCollection<BankTransaction> transactions = ...;
transactions.apply(
FileIO.<BankTransaction, TransactionType>writeDynamic()
.by(Transaction::getType)
.via(BankTransaction::toString, TextIO.sink())
.to("gs://bucket/myfolder/")
.withNaming(type -> defaultNaming("transactions_", ".txt"));
有关DynamicDestinations
使用的示例,请参阅example code in the TextIO unit tests。
或者,如果您想将每条记录写入自己的文件,只需使用FileSystems
中的FileSystems.create()
API(特别是DoFn
)。
答案 1 :(得分:1)
正如@anrope所述,apache_beam.io.fileio似乎是用于编写文件的最新Python API。由于使用了WriteToText类,因此WordCount示例目前已过时,该类继承自现已弃用的apache_beam.io.filebasedsink / apache_beam.io.iobase
要添加到现有答案中,这是我的管道,我在其中运行时动态命名输出文件。我的管道接收N个输入文件并创建N个输出文件,这些文件根据其相应的输入文件名进行命名。
with beam.Pipeline(options=pipeline_options) as p:
(p
| 'CreateFiles' >> beam.Create(input_file_paths)
| 'MatchFiles' >> MatchAll()
| 'OpenFiles' >> ReadMatches()
| 'LoadData' >> beam.Map(custom_data_loader)
| 'Transform' >> beam.Map(custom_data_transform)
| 'Write' >> custom_writer
)
当我加载数据时,我创建了一个元组记录(file_name, data)
的PCollection。我所有的变换都应用于data
,但是我将file_name
传递到管道的末尾以生成输出文件名。
def custom_data_loader(f: beam.io.fileio.ReadableFile):
file_name = f.metadata.path.split('/')[-1]
data = custom_read_function(f.open())
return file_name, data
def custom_data_transform(record):
file_name, data = record
data = custom_transform_function(data)
return file_name, data
然后我将文件保存为:
def file_naming(record):
file_name, data = record
file_name = custom_naming_function(file_name)
return file_name
def return_destination(*args):
"""Optional: Return only the last arg (destination) to avoid sharding name format"""
return args[-1]
pickle_writer = WriteToFiles(
path='path/to/output',
file_naming=return_destination,
destination=file_naming,
sink=TextSink()
)
使用您自己的逻辑替换所有custom_*
函数。
答案 2 :(得分:0)
对于Python人群:
在2.14.0 beam.io.fileio.WriteToFiles
的Beam python SDK中添加了实验性写法:
my_pcollection | beam.io.fileio.WriteToFiles(
path='/my/file/path',
destination=lambda record: 'avro' if record['type'] == 'A' else 'csv',
sink=lambda dest: AvroSink() if dest == 'avro' else CsvSink(),
file_naming=beam.io.fileio.destination_prefix_naming())
可用于每条记录写入不同的文件。
如果文件名基于集合中的数据,则可以使用destination
和file_naming
根据每条记录的数据创建文件。
更多文档在这里:
https://beam.apache.org/releases/pydoc/2.14.0/apache_beam.io.fileio.html#dynamic-destinations
此处还有JIRA问题:
答案 3 :(得分:0)
我知道这是一个古老的问题,但是我对文档中的示例感到困惑。
这是一个简单的示例,说明如何根据字典项拆分文件。
pipeline_options = PipelineOptions()
pipeline_options.view_as(SetupOptions).save_main_session = False
def file_names(*args):
file_name = fileio.destination_prefix_naming()(*args)
destination, *_ = file_name.split("----")
return f"{destination}.json"
class JsonSink(fileio.TextSink):
def write(self, element):
record = json.loads(element)
record.pop("id")
self._fh.write(json.dumps(record).encode("utf8"))
self._fh.write("\n".encode("utf8"))
def destination(element):
return json.loads(element)["id"]
with beam.Pipeline(options=pipeline_options) as p:
data = [
{"id": 0, "message": "whhhhhhyyyyyyy"},
{"id": 1, "message": "world"},
{"id": 1, "message": "hi there!"},
{"id": 1, "message": "what's up!!!?!?!!?"},
]
(
p
| "CreateEmails" >> beam.Create(data)
| "JSONify" >> beam.Map(json.dumps)
| "Write Files"
>> fileio.WriteToFiles(
path="path/",
destination=destination,
sink=lambda dest: JsonSink(),
file_naming=file_names,
)
)