7

As a response to this answer to a previous question of mine, I wrote the following short program to try and reproduce the problem.

from pptx import Presentation
from io import BytesIO

p = Presentation()
slide = p.slides.add_slide(p.slide_layouts[0])
slide.shapes[0].text = 'asdf'

p.save('test.pptx')

out = BytesIO()
p.save(out)

out_file = open('bytes_test.pptx', 'wb', buffering=0)
out_file.write(out.read())
out_file.close()

This produced two pptx files.

The first, test.pptx, contained a single slide with the "Title Slide" layout and containing the string "asdf". The file size was 28 KB.

The second, bytes_test.pptx, when opened in PowerPoint, showed only a large grey box that said "Click to add first slide". The file size was 0.

Running on Windows 10 with Anaconda Python 3.6.1 and python-pptx 0.6.6

Why does this happen?

MichaelPlante
  • 452
  • 1
  • 4
  • 14

1 Answers1

6

Well, a couple things I can think of, this might take some back-and-forth.

First, I would try using out.getvalue() instead of out.read(). That's how I've always done it and its documented behavior is to get the entire contents of the stream.

If that doesn't work, I would add out.flush() and out.seek(0) before the out.read() call. BytesIO is a buffered output stream, and its possible some buffered data isn't getting written to the stream before the read() call. Also, I expect read() works from the current cursor position, which the seek(0) call would reset to the beginning of the file.

Let us know how you go with that and we'll take it from there.

scanny
  • 26,423
  • 5
  • 54
  • 80
  • 1
    Switching to getvalue() worked. However, the issue now becomes the fact that the original application I was writing this for doesn't read the BytesIO object itself, but instead passes it to Flask's send_file method. I'll have to look around the Flask documentation a bit more to see what my options are. Thanks! – MichaelPlante Oct 27 '17 at 19:31
  • 1
    @MichaelPlante I expect it's looking for a sequence of bytes (binary string). In Python 3 I expect you can just pass it `bytes = out.getvalue()` and it will be happy. You may also need to pass it the MIME-type for a PowerPoint presentation ('application/vnd.openxmlformats-officedocument.presentationml.presentation') in order to get the desired download behavior. – scanny Oct 27 '17 at 20:23
  • 2
    Turned out that calling seek(0) and then passing the BytesIO to flask was enough. – MichaelPlante Oct 30 '17 at 12:00