Currently, I’m just serving files like this:
# view callable
def export(request):
response = Response(content_type='application/csv')
# use datetime in filename to avoid collisions
f = open('/temp/XML_Export_%s.xml' % datetime.now(), 'r')
# this is where I usually put stuff in the file
response.app_iter = f
response.headers['Content-Disposition'] = ("attachment; filename=Export.xml")
return response
The problem with this is that I can’t close or, even better, delete the file after the response has been returned. The file gets orphaned. I can think of some hacky ways around this, but I’m hoping there’s a standard way out there somewhere. Any help would be awesome.
You do not want to set a file pointer as the
app_iter. This will cause the WSGI server to read the file line by line (same asfor line in file), which is typically not the most efficient way to control a file upload (imagine one character per line). Pyramid’s supported way of serving files is viapyramid.response.FileResponse. You can create one of these by passing a file object.Another option is to pass a file pointer to
app_iterbut wrap it in thepyramid.response.FileIterobject, which will use a sane block size to avoid just reading the file line by line.The WSGI specification has strict requirements that response iterators which contain a
closemethod will be invoked at the end of the response. Thus settingresponse.app_iter = open(...)should not cause any memory leaks. BothFileResponseandFileIteralso support aclosemethod and will thus be cleaned up as expected.As a minor update to this answer I thought I’d explain why
FileResponsetakes a file path and not a file pointer. The WSGI protocol provides servers an optional ability to provide an optimized mechanism for serving static files viaenviron['wsgi.file_wrapper'].FileResponsewill automatically handle this if your WSGI server has provided that support. With this in mind, you find it to be a win to save your data to a tmpfile on a ramdisk and providing theFileResponsewith the full path, instead of trying to pass a file pointer toFileIter.http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/api/response.html#pyramid.response.FileResponse