How to download bytesio png file after callback

I have a FileDownload button that has a callback attached to it, the call back generates the file as a BytesIO object

(I’m taking inspiration from the StringIO csv example at Filedownload — Panel 0.11.3 documentation):

class imageDownload():
    
    def __init__(self, title, pane=None):
    
        def png_button_callback(*events):
            from io import BytesIO
            bio = BytesIO()
            bio.filename = self.png_button.filename
            self.pane.save(bio)
            bio.seek(0)
            return bio

        self.pane = pane or pn.Row()
        self.now = dt.datetime.now().strftime("%Y-%m-%d.%H.%M.%S")
        self.filename = f'{title} {self.now}.png'
        self.png_button = pn.widgets.FileDownload(
            callback=png_button_callback,
            filename=self.filename,
            file=f'/tmp/{self.filename}')

I implement it in my app this way:

...
image_button = imageDownload(title='image', pane=summary)
template.sidebar.append(image_button.png_button)
...

I implement it this way (perhaps there’s an easier way) just so I can make changes to the pane via widgets, then image that instead of whatever the pane looks like on load.

Ok, so it seems to work, but doesn’t download the file as a valid png format:

So, my question is, is there something about pane.save() that precludes this? Perhaps there’s a better way to solve this all around? Thanks for your help!

I suspect that you are saving an HTML file with a .png extension, so could you try to rename the png file to test.html and see if it open in the browser?

1 Like

yes that was it! any way you know of to tell pane.save() that it’s a png file instead, I know it works off the filename but it doesn’t seem to detect bio.filename in this situation.

I don’t know. However, I would do something like this:

with open(bio, "wb") as file:
    file.write(self.pane._img())
1 Like