如何在Python 3中将演示文稿保存到类文件对象?

7

Python 3已经用io.StringIO替代了StringIO.StringIO。我之前使用前者成功保存演示文稿,但是似乎在后者上无法正常工作。

from pptx import Presentation
from io import StringIO

presentation = Presentation('presentation.pptx')
output = StringIO()
presentation.save(output)

以上代码产生的结果为:
Traceback (most recent call last): File "", line 1, in File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\presentation.py", line 46, in save self.part.save(file) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\parts\presentation.py", line 118, in save self.package.save(path_or_stream) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\package.py", line 166, in save PackageWriter.write(pkg_file, self.rels, self.parts) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\pkgwriter.py", line 33, in write PackageWriter._write_content_types_stream(phys_writer, parts) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\pkgwriter.py", line 47, in _write_content_types_stream phys_writer.write(CONTENT_TYPES_URI, content_types_blob) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\phys_pkg.py", line 156, in write self._zipf.writestr(pack_uri.membername, blob) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\zipfile.py", line 1645, in writestr with self.open(zinfo, mode='w') as dest: File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\zipfile.py", line 1349, in open return self._open_to_write(zinfo, force_zip64=force_zip64) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\zipfile.py", line 1462, in _open_to_write self.fp.write(zinfo.FileHeader(zip64)) TypeError: string argument expected, got 'bytes'
是否有办法在Python 3中将演示文稿保存到类似文件的对象中,或者我必须在此项目中使用Python 2?

你有完整的堆栈跟踪吗? - Patrick Haugh
1
我猜 presentation.save 调用了它的参数的 .write 方法。在你的情况下,StringIO#write 接受字符串而不是字节,因此出现了错误。使用 BytesIO 是正确的方法。 - bfontaine
在完整的堆栈跟踪中编辑。我尝试过BytesIO,但它生成了一个空的PowerPoint。 - MichaelPlante
3个回答

5

BytesIO()怎么样?

from pptx import Presentation
from io import BytesIO

presentation = Presentation('presentation.pptx')
output = BytesIO()
presentation.save(output)
output.seek(0)
# from here do what you like with output, e.g. pass it to something expecting bytes with output.read()

这样至少可以消除错误。

3
那是我的第一直觉。使用BytesIO给了我一个空白的PowerPoint文件。 - MichaelPlante
@MichaelPlante:我想我知道为什么你会得到一个空白 - 在保存演示文稿后,BytesIO缓冲区的位置在末尾。在执行output.read()之前,先执行output.seek(0)。 我会为下一个遇到这个问题的人编辑回复。 - Michel Müller
1
@MichelMüller 我使用 getvalue() 而不是 read() 成功解决了这个问题。详细信息可以在这里找到。 - MichaelPlante

4
汉努的回答非常正确,这正是用于验证python-pptx测试套件中此行为的代码:
stream = BytesIO()
presentation.save(stream)

https://github.com/scanny/python-pptx/blob/master/features/steps/presentation.py#L105

如果代码给你一个空白的演示文稿,那么可能发生了其他问题。我会重现这种行为,使其稳定和可重复,并提出类似“为什么我的演示文稿是空白的?”的问题在另一个SO问题中,同时发布完整的最小代码以产生该行为。
这已经是我第二次听到这样的事情,这让我怀疑在幕后可能正在发生某种半系统化的情况来产生这种行为。但与此同时,你几乎不可能以尝试保存到流的部分失败而得到一个完全工作的演示文稿,只是幻灯片为空的情况。
可能导致这种情况的常见情况是保存新打开的默认演示文稿,例如:
prs = Presentation()
output = BytesIO()
prs.save(output)

当然,这不是你有意做的事情,但很容易出现错误,所以我想提一下。
如果您能帮助我们重复您的结果,我们将解决它 :)

谢谢。新问题已发布 - MichaelPlante

2

在开发类似CGI的演示时,我遇到了同样的问题。我的pptx应该作为文件发送给用户。您可以使用BytesIO而不是StringIO将pptx作为文件发送。

qs = cgi.FieldStorage()
link = qs.getfirst('link', 'default_link_for_debug')

...
...

target_stream = BytesIO()
prs.save(target_stream)
target_stream.seek(0) # important!

length = target_stream.getbuffer().nbytes
buffer = target_stream.getbuffer()
#buffer = target_stream.read() # it also works

if 'HTTP_HOST' in os.environ:
    sys.stdout.buffer.write(b'Content-Type: application/vnd.openxmlformats-officedocument.presentationml.presentation\r\n')
    sys.stdout.buffer.write('Content-Disposition: attachment; filename="offer-{0}.pptx"\r\n'.format(link).encode('ascii'))
    sys.stdout.buffer.write('Content-Length: {0}\r\n'.format(length).encode('ascii'))
    sys.stdout.buffer.write(b'Pragma: no-cache\r\n')
    sys.stdout.buffer.write(b'\r\n')
    sys.stdout.buffer.write(buffer)

else: # for debug
    with open("offer-{0}.pptx".format(link),'wb') as out: 
        out.write(buffer) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接