Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use pyramid.response.FileIter

Tags:

python

pyramid

I have the following view code that attempts to "stream" a zipfile to the client for download:

import os
import zipfile
import tempfile
from pyramid.response import FileIter

def zipper(request):
    _temp_path = request.registry.settings['_temp']
    tmpfile = tempfile.NamedTemporaryFile('w', dir=_temp_path, delete=True)

    tmpfile_path = tmpfile.name

    ## creating zipfile and adding files
    z = zipfile.ZipFile(tmpfile_path, "w")
    z.write('somefile1.txt')
    z.write('somefile2.txt')
    z.close()

    ## renaming the zipfile
    new_zip_path = _temp_path + '/somefilegroup.zip'
    os.rename(tmpfile_path, new_zip_path)

    ## re-opening the zipfile with new name
    z = zipfile.ZipFile(new_zip_path, 'r')
    response = FileIter(z.fp)

    return response

However, this is the Response I get in the browser:

Could not convert return value of the view callable function newsite.static.zipper into a response object. The value returned was .

I suppose I am not using FileIter correctly.


UPDATE:

Since updating with Michael Merickel's suggestions, the FileIter function is working correctly. However, still lingering is a MIME type error that appears on the client (browser): Resource interpreted as Document but transferred with MIME type application/zip: "http://newsite.local:6543/zipper?data=%7B%22ids%22%3A%5B6%2C7%5D%7D"

To better illustrate the issue, I have included a tiny .py and .pt file on Github: https://github.com/thapar/zipper-fix

like image 439
Raj Avatar asked Jan 13 '23 17:01

Raj


1 Answers

FileIter is not a response object, just like your error message says. It is an iterable that can be used for the response body, that's it. Also the ZipFile can accept a file object, which is more useful here than a file path. Let's try writing into the tmpfile, then rewinding that file pointer back to the start, and using it to write out without doing any fancy renaming.

import os
import zipfile
import tempfile
from pyramid.response import FileIter

def zipper(request):
    _temp_path = request.registry.settings['_temp']
    fp = tempfile.NamedTemporaryFile('w+b', dir=_temp_path, delete=True)

    ## creating zipfile and adding files
    z = zipfile.ZipFile(fp, "w")
    z.write('somefile1.txt')
    z.write('somefile2.txt')
    z.close()

    # rewind fp back to start of the file
    fp.seek(0)

    response = request.response
    response.content_type = 'application/zip'
    response.app_iter = FileIter(fp)
    return response

I changed the mode on NamedTemporaryFile to 'w+b' as per the docs to allow the file to be written to and read from.

like image 183
Michael Merickel Avatar answered Jan 25 '23 14:01

Michael Merickel