2010-08-27 8 views
7

ich den folgenden django/Python-Code unter Verwendung eine Datei an den Browser zu streamen:Entfernen tmp-Datei nach der Rückkehr Httpresponse in django

wrapper = FileWrapper(file(path)) 
response = HttpResponse(wrapper, content_type='text/plain') 
response['Content-Length'] = os.path.getsize(path) 
return response 

Gibt es eine Möglichkeit, die Datei zu löschen, nachdem die reponse zurückgegeben wird? Verwenden einer Rückruffunktion oder etwas? Ich könnte nur ein Cron machen, um alle tmp-Dateien zu löschen, aber es wäre besser, wenn ich Dateien streamen und sie auch von derselben Anfrage löschen könnte.

Antwort

7

können Sie einen NamedTemporaryFile verwenden:

from django.core.files.temp import NamedTemporaryFile 
def send_file(request): 
    newfile = NamedTemporaryFile(suffix='.txt') 
    # save your data to newfile.name 
    wrapper = FileWrapper(newfile) 
    response = HttpResponse(wrapper, content_type=mime_type) 
    response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(modelfile.name) 
    response['Content-Length'] = os.path.getsize(modelfile.name) 
    return response 

temporäre Datei gelöscht werden soll, sobald die newfile Objekt vertrieben wird.

+0

'Temporäre Datei sollte gelöscht werden, sobald das Newfile-Objekt entfernt wird': Gibt es einen eingebauten Mechanismus zum automatischen Löschen von' NamedTemporaryFile'-Instanzen? –

+0

Wenn ich richtig bin, werden Objekte vom Garbage Collector zerstört, sobald alle Referenzen darauf zerstört sind. Wenn Sie die Funktion send_file verlassen, sollte kein Verweis mehr auf das newfile-Objekt vorhanden sein. Daher könnte es beim nächsten Ausführen des GC entfernt werden. Der Destruktor von NamedTemporaryFile heißt es: def close (self): wenn nicht self.close_called: self.close_called = True self.file.close() self.unlink (self.name) def __del __ (Selbst): self.close() – fylb

+0

fylb, Sie haben Recht, aber es ist nicht garantiert, dass das Objekt Müll-gesammelt und seine __del__ Methode aufgerufen wird. Wer weiß, was der Müllsammler tun wird? Besser regelmäßig manuell aufräumen. – loevborg

0

Eine Möglichkeit wäre, eine Ansicht hinzuzufügen, diese Datei zu löschen und von die Client-Seite rief einen asynchronen Aufruf mit (XMLHttpRequest). Eine Variante davon würde das Zurückmelden vom Client beim Erfolg beinhalten, so dass der Server diese Datei zum Löschen markieren kann und ein periodischer Job sie bereinigen kann.

+1

Klingt nicht wie eine gute Idee für mich - es gibt keine Notwendigkeit für eine zusätzliche Nachricht vom Client zum Server.Die temporären Bereinigungen der temporären Dateien sind viel besser. – loevborg

+1

@loevborg: Das OP * bat * um Alternativen. Daher. 'Ich könnte nur ein Cron machen, um alle tmp-Dateien zu löschen, aber es wäre besser ...' –

2

Meistens verwenden wir periodische Cron-Jobs.

Django hat bereits einen Cron-Job, um verlorene Sitzungen zu bereinigen. Und du fährst es schon, oder?

Siehe http://docs.djangoproject.com/en/dev/topics/http/sessions/#clearing-the-session-table

Sie wollen einen anderen Befehl wie dieser, in der Anwendung, die alten Dateien bereinigt.

Sehen Sie diese http://docs.djangoproject.com/en/dev/howto/custom-management-commands/

Auch Sie nicht wirklich kann diese Datei von Django schicken. Manchmal können Sie eine bessere Leistung erzielen, indem Sie die Datei in einem von Apache verwendeten Verzeichnis erstellen und auf eine URL umleiten, damit die Datei von Apache für Sie bereitgestellt werden kann. Manchmal ist das schneller. Die Bereinigung wird jedoch nicht besser gehandhabt.

+0

Anstatt Redirect, können Sie 'mod xsendfile' mit Apache verwenden, dann ist es eine Anfrage und Sie können den Dateizugriff steuern. – Mark

2

Dies wird nur mit dem regulären Python-Ansatz (sehr einfaches Beispiel):

# something generates a file at filepath 

from subprocess import Popen 

# open file 
with open(filepath, "rb") as fid: 
    filedata = fid.read() 

# remove the file 
p = Popen("rm %s" % filepath, shell=True) 

# make response 
response = HttpResponse(filedata, content-type="text/plain") 

return response 
1

Für zukünftige Referenzen: Ich hatte gerade den Fall, dass ich nicht temporäre Dateien für Downloads verwenden könnte. Aber ich musste sie immer noch löschen; also hier ist, wie ich es gemacht habe (ich wollte mich wirklich nicht auf Cron Jobs oder Sellerie oder Wossnamen verlassen, es ist ein sehr kleines System und ich wollte, dass es so bleibt).

def plug_cleaning_into_stream(stream, filename): 
    try: 
     closer = getattr(stream, 'close') 
     #define a new function that still uses the old one 
     def new_closer(): 
      closer() 
      os.remove(filename) 
      #any cleaning you need added as well 
     #substitute it to the old close() function 
     setattr(stream, 'close', new_closer) 
    except: 
     raise 

und dann nahm ich einfach den Stream für die Antwort verwendet und in ihn eingesteckt.

def send_file(request, filename): 
    with io.open(filename, 'rb') as ready_file: 
     plug_cleaning_into_stream(ready_file, filename) 
     response = HttpResponse(ready_file.read(), content_type='application/force-download') 
     # here all the rest of the heards settings 
     # ... 
     return response 

Ich weiß, das ist schnell und schmutzig, aber es funktioniert. Ich bezweifle, dass es für einen Server mit Tausenden von Anfragen pro Sekunde produktiv wäre, aber das ist hier nicht der Fall (max ein paar Dutzend pro Minute).

EDIT: vergessen zu präzisieren, dass ich es mit sehr sehr großen Dateien zu tun hatte, die nicht in den Speicher während des Downloads passen konnten. Deshalb verwende ich eine BufferedReader (was ist unter io.open())

Verwandte Themen