我试图使用requests、lxml和{a4}使用ExchangeGetAttachmentweb服务。此服务在soapxmlhttp响应中返回base64编码的文件。文件内容包含在单个XML元素的单行中。GetAttachment
只是一个例子,但问题更普遍。在
我想将解码后的文件内容直接流式传输到磁盘,而不必在任何时候将附件的全部内容存储在内存中,因为一个附件可能有几个100 MB。在
我试过这样的方法:
r = requests.post('https://example.com/EWS/Exchange.asmx', data=..., stream=True)
with open('foo.txt', 'wb') as f:
for action, elem in lxml.etree.iterparse(GzipFile(fileobj=r.raw)):
if elem.tag == 't:Content':
b64_encoder = Base64IO(BytesIO(elem.text))
f.write(b64_encoder.read())
但是lxml
仍然将附件的副本存储为elem.text
。我有什么方法可以创建一个完全流式的XML解析器,它也可以直接从输入流中流式处理元素的内容?在
在这种情况下不要使用
iterparse
。iterparse()
方法只能发出元素开始和结束事件,因此在找到结束的XML标记后,元素中的任何文本都将被提供给您。在相反,请使用SAX parser interface。这是XML解析库的通用标准,用于将解析后的数据传递给内容处理程序。^{} callback 以块的形式传递字符数据(假设实现XML库实际上利用了这种可能性)。这是一个来自elementtreeapi的低级API,Python标准库已经绑定了Expat解析器来驱动它。在
所以流程就变成了:
GzipFile
中,以便于解压缩。或者,更好的方法是设置response.raw.decode_content = True
,并根据服务器设置的内容编码将解压缩留给请求库。在GzipFile
实例或原始流传递给使用^{make_parser()
,您首先可以启用诸如命名空间处理之类的功能(这可以确保在Exchange决定更改每个名称空间使用的短前缀时代码不会中断)。在characters()
方法是用XML数据块调用的;请检查元素start事件是否正确,这样就知道何时需要base64数据。您可以一次解码chunks of (a multiple of) 4 characters中的base64数据,并将其写入文件。我不会在这里使用base64io
,只需要自己进行分块。在简单的内容处理程序可以是:
你可以这样使用它:
^{pr2}$这将以64KB的块(默认值为^{} buffer size )解析输入XML,因此附件数据最多解码为48KB的原始数据块。在
我可能会扩展内容处理程序以获取目标目录,然后查找
<t:Name>
元素来提取文件名,然后使用该元素将数据提取到找到的每个附件的正确文件名中。您还需要验证您是否确实在处理GetAttachmentResponse
文档,并处理错误响应。在相关问题 更多 >
编程相关推荐