python - multiple - Servir archivos grandes(con cargas altas) en Django
object level permissions django (5)
A menos que vaya a atender un número muy pequeño de tales solicitudes, cualquier solución que requiera la entrega de su contenido a través de django no será escalable. Para poder escalar cualquier cosa en el futuro, probablemente querrá trasladar su almacenamiento de contenido y su servicio a un servidor separado y esto no funcionará.
La forma recomendada sería mantener el contenido estático servido a través de un servidor más ligero (como nginx). Para agregar seguridad, pase un token del servidor estático desde django configurando la cookie o mediante los parámetros de obtención.
El token debe tener los siguientes valores: marca de tiempo, nombre de archivo, ID de usuario. Debe estar firmado a través de alguna clave por la aplicación django.
A continuación, escriba un pequeño módulo nginx que verifique el token y que el usuario tenga acceso al archivo. También debe verificar que el token no sea lo suficientemente antiguo al verificar la marca de tiempo.
He estado usando un método para servir descargas pero como no era seguro decidí cambiarlo. (El método era un enlace al archivo original almacenado, ¡pero el riesgo era que todos los que tenían el enlace pudieran haber descargado el archivo!), así que ahora sirvo el archivo a través de mis vistas, de esa manera solo los usuarios con permiso pueden descargar el archivo, pero estoy notando una alta carga en el servidor mientras hay muchas solicitudes de descarga simultáneas para los archivos. Aquí hay parte de mi código que maneja las descargas para los usuarios (considera que el archivo es una imagen)
image = Image.open ("the path to file")
response = HttpResponse(mimetype = ''image/png'' )
response[''Content-Disposition''] = ''attachment: filename=%s.png'' % filename
image.save(response , "png")
return response
¿Existe alguna forma mejor de servir archivos mientras se mantiene la seguridad y se reduce la carga lateral del servidor? gracias por adelantado :)
Es mejor usar FileRespose, es una subclase de StreamingHttpResponse optimizado para archivos binarios. Utiliza wsgi.file_wrapper si lo proporciona el servidor wsgi; de lo contrario, transmite el archivo en fragmentos pequeños.
import os
from django.http import FileResponse
from django.core.servers.basehttp import FileWrapper
def download_file(request):
_file = ''/folder/my_file.zip''
filename = os.path.basename(_file)
response = FileResponse(FileWrapper(file(filename, ''rb'')), content_type=''application/x-zip-compressed'')
response[''Content-Disposition''] = "attachment; filename=%s" % _file
return response
FileWrapper no funcionará cuando se instale GZipMiddleware (Django 1.4 y versiones anteriores): https://code.djangoproject.com/ticket/6027
Si usa GZipMiddleware, una solución práctica es escribir una subclase de FileWrapper así:
from wsgiref.util import FileWrapper
class FixedFileWrapper(FileWrapper):
def __iter__(self):
self.filelike.seek(0)
return self
import mimetypes, os
my_file = ''/some/path/xy.ext''
response = HttpResponse(FixedFileWrapper(open(my_file, ''rb'')), content_type=mimetypes.guess_type(my_file)[0])
response[''Content-Length''] = os.path.getsize(my_file)
response[''Content-Disposition''] = "attachment; filename=%s" % os.path.basename(my_file)
return response
A partir de Python 2.5, no es necesario importar FileWrapper desde Django.
Puede usar el método ''sendfile'' como se describe en esta answer .
Prácticamente necesitas esto (c & p):
response = HttpResponse(mimetype=''application/force-download'')
response[''Content-Disposition''] = ''attachment; filename=%s'' % smart_str(file_name)
response[''X-Sendfile''] = smart_str(path_to_file)
# It''s usually a good idea to set the ''Content-Length'' header too.
# You can also set any other required headers: Cache-Control, etc.
return response
Esto requiere mod_xsendfile (que también es compatible con nginx o lighty )
Su apertura de la imagen la carga en la memoria y esto es lo que causa el aumento de la carga en uso intenso. Como lo publicó Martin, la verdadera solución es entregar el archivo directamente.
Aquí hay otro enfoque, que transmitirá su archivo en trozos sin cargarlo en la memoria.
import os
import mimetypes
from django.http import StreamingHttpResponse
from django.core.servers.basehttp import FileWrapper
def download_file(request):
the_file = ''/some/file/name.png''
filename = os.path.basename(the_file)
chunk_size = 8192
response = StreamingHttpResponse(FileWrapper(open(the_file, ''rb''), chunk_size),
content_type=mimetypes.guess_type(the_file)[0])
response[''Content-Length''] = os.path.getsize(the_file)
response[''Content-Disposition''] = "attachment; filename=%s" % filename
return response