2010-08-27 13 views
7

estoy usando el siguiente código de Django/Python para transmitir un archivo al navegador:Extracción de archivos tmp después de HttpResponse de retorno en Django

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

¿Hay una manera de eliminar el archivo después se devuelve el Reponse? ¿Usando una función de devolución de llamada o algo así? Podría hacer un cron para eliminar todos los archivos tmp, pero sería mejor si pudiera transmitir archivos y eliminarlos también de la misma solicitud.

Respuesta

7

Puede utilizar un NamedTemporaryFile:

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 

archivo temporal debe ser borrado una vez se desaloja el objeto nuevofichero.

+0

'el archivo temporal debe eliminarse una vez que se ha expulsado el objeto newfile': ¿hay un mecanismo incorporado para eliminar automáticamente las instancias' NamedTemporaryFile'? –

+0

si estoy en lo cierto, los objetos son destruidos por el recolector de basura una vez que se destruyen todas las referencias a él. Cuando salga de la función send_file, no debería haber más referencias al objeto newfile, por lo tanto, podría eliminarse la próxima vez que se ejecute el GC. El destructor de NamedTemporaryFile afirma: def cerca de (auto): si no self.close_called: self.close_called = True self.file.close() self.unlink (self.name) def __del __ (self): self.close() – fylb

+0

fylb, tienes razón, pero no está garantizado que el objeto será recogido como basura y se invoca su método __del__. ¿Quién sabe qué hará el recolector de basura? Es mejor limpiarlo manualmente de forma periódica. – loevborg

0

Una forma sería agregar una vista para eliminar este archivo y llamarlo desde el desde el lado del cliente usando una llamada asincrónica (XMLHttpRequest). Una variante de esto implicaría informar al cliente sobre el éxito para que el servidor pueda marcar este archivo para su eliminación y tener un trabajo periódico de limpieza.

+1

No me parece una buena idea, no hay necesidad de un mensaje adicional del cliente al servidor.Limpiar los archivos temporales periódicamente es mucho mejor. – loevborg

+1

@loevborg: El OP * preguntó * por alternativas. Por lo tanto. 'Podría hacer que un cron elimine todos los archivos tmp, pero sería más nítido ...' –

2

Principalmente, utilizamos trabajos de cron periódicos para esto.

Django ya tiene un trabajo de cron para limpiar las sesiones perdidas. Y ya lo estás ejecutando, ¿verdad?

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

¿Quieres otro comando al igual que éste, en su aplicación, que limpia los archivos antiguos.

ver este http://docs.djangoproject.com/en/dev/howto/custom-management-commands/

Además, es posible que en realidad no ser el envío de este archivo de Django. A veces puede obtener un mejor rendimiento creando el archivo en un directorio utilizado por Apache y redirigiendo a una URL para que Apache le proporcione el archivo. A veces esto es más rápido. Sin embargo, no maneja la limpieza mejor.

+0

En lugar de redireccionar, puede usar 'mod xsendfile' con Apache, luego es una solicitud y puede controlar el acceso al archivo. – Mark

2

Esto es sólo con el método ordinario pitón (ejemplo muy sencillo):

# 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

Para futuras referencias: acabo de tener el caso en el que no podía usar archivos temporales para las descargas. Pero igual tenía que eliminarlos después; así que así es como lo hice (realmente no quería confiar en cron jobs o apio o wossnames, es un sistema muy pequeño y quería que siguiera siendo así).

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 

y luego acabo de tomar el flujo utilizado para la respuesta y enchufado en él.

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 

Sé que esto es rápido y sucio, pero funciona. Dudo que sea productivo para un servidor con miles de solicitudes por segundo, pero ese no es mi caso aquí (máximo unas pocas docenas por minuto).

EDITAR: Olvidé precisar que estaba tratando con archivos muy grandes que no cabían en la memoria durante la descarga. Es por eso que estoy usando un BufferedReader (que es lo que está debajo de io.open())

Cuestiones relacionadas