Reputation: 59405
I need to create a file on the server, serve it to the client, and I would prefer to delete the file from the server afterwards.
Here's what I'm currently trying:
def myaction():
fs = facts.generatefacts(int(low),int(high),int(amount),op)
filename = 'test.txt'
FILE = open(filename,'w')
FILE.writelines('my\nstuff\nhere')
FILE.close()
RETURN_FILE = open(filename,'r')
return serve_fileobj(RETURN_FILE,disposition='attachment',
content_type='.txt',name=filename)
myaction.exposed = True
There are several things about this that I don't like. I wouldn't think that I would have to open the file twice, for example. I would expect that there is a way to write content directly to the response object, without ever creating a file object, but that isn't my question today.
The above code accomplishes what I want, but leaves a file behind. If I delete the file before returning the response, then (of course) the file isn't found.
Is there a way to remove this file as soon as it has been served?
I'm coming from the Java world, so I'm a bit disoriented, and any other suggestions to improve the above are appreciated.
Upvotes: 1
Views: 3962
Reputation: 11
This worked for me:
class Handler:
...
def download_complete(self)
os.unlink(cherrypy.request.zipFileName)
def download(self, path)
zipFileName = createZip(path)
cherrypy.request.zipFileName = zipFileName
cherrypy.request.hooks.attach('on_end_request', self.download_complete)
return cherrypy.lib.static.serve_download(zipFileName)
Upvotes: 0
Reputation: 711
The solution below uses weak references to clean up temporary files (and in this case temporary directories) once the file is fully sent by cherrypy.
I've used temporary directories, since that allows for processes that may create several files before sending the final result (eg, returning a zipped up excel file for example), but for simple cases just a single temporary file would work fine.
import cherrypy
from cherrypy import expose
import zipfile
import weakref
import shutil
import os
import tempfile
PARENT_TEMP_DATA_DIR = '/tmp/cherrypy_data_files'
def single_file_zip(filepath):
'Give a filepath, creates a zip archive in the same directory, with just the single file inside'
filename = os.path.basename(filepath)
zipname = '%s.zip' % os.path.splitext(filename)[0]
if filename.lower() == zipname.lower():
raise ValueError("Can't use .zip file as source")
zippath = os.path.join(os.path.dirname(filepath), zipname)
zf = zipfile.ZipFile(zippath, mode='w', compression=zipfile.ZIP_DEFLATED)
zf.write(filepath, filename)
zf.close()
return zippath
class DataFiles(object):
def __init__(self):
self.weak_references = {}
def cleanup(self, wr):
if wr in self.weak_references:
filepath = self.weak_references[wr]
if os.path.isfile(filepath):
try:
os.remove(filepath)
except Exception:
pass
if os.path.isdir(filepath):
shutil.rmtree(filepath, ignore_errors=True)
self.weak_references.pop(wr)
@expose
def index(self):
tempdir = os.path.abspath(tempfile.mkdtemp(dir=PARENT_TEMP_DATA_DIR))
txt_path = os.path.join(tempdir, 'my_data.txt')
with open(txt_path, 'wb') as fh:
fh.write('lots of data here\n')
zip_path = single_file_zip(txt_path)
os.remove(txt_path) # not strictly needed, as the cleanup routine would remove this anyway
result = cherrypy.lib.static.serve_download(zip_path)
# the weak-reference allows automatic cleanup of the temp dir once the file is served
wr = weakref.ref(result, self.cleanup)
self.weak_references[wr] = tempdir
return result
if __name__=='__main__':
# on startup clean up any prior temporary data
tempdir_parent = PARENT_TEMP_DATA_DIR
print 'Clearing %s' % tempdir_parent
if not os.path.exists(tempdir_parent):
os.mkdir(tempdir_parent)
for filename in os.listdir(tempdir_parent):
filepath = os.path.join(tempdir_parent, filename)
if os.path.isfile(filepath):
print 'Deleting file %s' % filepath
os.remove(filepath)
if os.path.isdir(filepath):
print 'Removing directory %s and all contents' % filepath
shutil.rmtree(filepath, ignore_errors=True)
# start CherryPy
cherrypy.quickstart(DataFiles())
Upvotes: 1
Reputation: 1331
1) You can move file to temporary folder and remove all files older 0.5 hours
2) You can try
result = serve_fileobj(RETURN_FILE,disposition='attachment',
content_type='.txt',name=filename)
os.unlink(filename)
return result
3) Try to use StringIO file object that can wrap string to look like a file.
Upvotes: 2