Implement sendfile.
This commit is contained in:
parent
c7f09d3fef
commit
f0a8689712
@ -1,17 +0,0 @@
|
|||||||
import os.path
|
|
||||||
|
|
||||||
from django.views.static import serve
|
|
||||||
|
|
||||||
|
|
||||||
def sendfile(request, filename, **kwargs):
|
|
||||||
"""
|
|
||||||
Send file using Django dev static file server.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Do not use in production. This is only to be used when developing and
|
|
||||||
is provided for convenience only
|
|
||||||
"""
|
|
||||||
dirname = os.path.dirname(filename)
|
|
||||||
basename = os.path.basename(filename)
|
|
||||||
return serve(request, basename, dirname)
|
|
@ -1,17 +0,0 @@
|
|||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django.http import HttpResponse
|
|
||||||
|
|
||||||
from ..utils import _convert_file_to_url
|
|
||||||
|
|
||||||
|
|
||||||
def sendfile(request, filename, **kwargs):
|
|
||||||
response = HttpResponse()
|
|
||||||
response['Location'] = _convert_file_to_url(filename)
|
|
||||||
# need to destroy get_host() to stop django
|
|
||||||
# rewriting our location to include http, so that
|
|
||||||
# mod_wsgi is able to do the internal redirect
|
|
||||||
request.get_host = lambda: ''
|
|
||||||
request.build_absolute_uri = lambda location: location
|
|
||||||
|
|
||||||
return response
|
|
@ -1,12 +0,0 @@
|
|||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django.http import HttpResponse
|
|
||||||
|
|
||||||
from ..utils import _convert_file_to_url
|
|
||||||
|
|
||||||
|
|
||||||
def sendfile(request, filename, **kwargs):
|
|
||||||
response = HttpResponse()
|
|
||||||
response['X-Accel-Redirect'] = _convert_file_to_url(filename)
|
|
||||||
|
|
||||||
return response
|
|
@ -1,60 +0,0 @@
|
|||||||
from email.utils import mktime_tz, parsedate_tz
|
|
||||||
import re
|
|
||||||
|
|
||||||
from django.core.files.base import File
|
|
||||||
from django.http import HttpResponse, HttpResponseNotModified
|
|
||||||
from django.utils.http import http_date
|
|
||||||
|
|
||||||
|
|
||||||
def sendfile(request, filepath, **kwargs):
|
|
||||||
'''Use the SENDFILE_ROOT value composed with the path arrived as argument
|
|
||||||
to build an absolute path with which resolve and return the file contents.
|
|
||||||
|
|
||||||
If the path points to a file out of the root directory (should cover both
|
|
||||||
situations with '..' and symlinks) then a 404 is raised.
|
|
||||||
'''
|
|
||||||
statobj = filepath.stat()
|
|
||||||
|
|
||||||
# Respect the If-Modified-Since header.
|
|
||||||
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
|
|
||||||
statobj.st_mtime, statobj.st_size):
|
|
||||||
return HttpResponseNotModified()
|
|
||||||
|
|
||||||
with File(filepath.open('rb')) as f:
|
|
||||||
response = HttpResponse(f.chunks())
|
|
||||||
|
|
||||||
response["Last-Modified"] = http_date(statobj.st_mtime)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def was_modified_since(header=None, mtime=0, size=0):
|
|
||||||
"""
|
|
||||||
Was something modified since the user last downloaded it?
|
|
||||||
|
|
||||||
header
|
|
||||||
This is the value of the If-Modified-Since header. If this is None,
|
|
||||||
I'll just return True.
|
|
||||||
|
|
||||||
mtime
|
|
||||||
This is the modification time of the item we're talking about.
|
|
||||||
|
|
||||||
size
|
|
||||||
This is the size of the item we're talking about.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if header is None:
|
|
||||||
raise ValueError
|
|
||||||
matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header,
|
|
||||||
re.IGNORECASE)
|
|
||||||
header_date = parsedate_tz(matches.group(1))
|
|
||||||
if header_date is None:
|
|
||||||
raise ValueError
|
|
||||||
header_mtime = mktime_tz(header_date)
|
|
||||||
header_len = matches.group(3)
|
|
||||||
if header_len and int(header_len) != size:
|
|
||||||
raise ValueError
|
|
||||||
if mtime > header_mtime:
|
|
||||||
raise ValueError
|
|
||||||
except (AttributeError, ValueError, OverflowError):
|
|
||||||
return True
|
|
||||||
return False
|
|
@ -1,9 +0,0 @@
|
|||||||
from django.http import HttpResponse
|
|
||||||
|
|
||||||
|
|
||||||
def sendfile(request, filename, **kwargs):
|
|
||||||
filename = str(filename)
|
|
||||||
response = HttpResponse()
|
|
||||||
response['X-Sendfile'] = filename
|
|
||||||
|
|
||||||
return response
|
|
@ -7,7 +7,6 @@ from django.core.files.base import ContentFile
|
|||||||
from django.db import transaction, IntegrityError
|
from django.db import transaction, IntegrityError
|
||||||
from django.db.models import Q, QuerySet
|
from django.db.models import Q, QuerySet
|
||||||
from fastapi import APIRouter, Depends, status, Request
|
from fastapi import APIRouter, Depends, status, Request
|
||||||
from fastapi.responses import FileResponse
|
|
||||||
|
|
||||||
from django_etebase import models
|
from django_etebase import models
|
||||||
from .authentication import get_authenticated_user
|
from .authentication import get_authenticated_user
|
||||||
@ -26,6 +25,7 @@ from .utils import (
|
|||||||
PERMISSIONS_READWRITE,
|
PERMISSIONS_READWRITE,
|
||||||
)
|
)
|
||||||
from .dependencies import get_collection_queryset, get_item_queryset, get_collection
|
from .dependencies import get_collection_queryset, get_item_queryset, get_collection
|
||||||
|
from .sendfile import sendfile
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
collection_router = APIRouter(route_class=MsgpackRoute, responses=permission_responses)
|
collection_router = APIRouter(route_class=MsgpackRoute, responses=permission_responses)
|
||||||
@ -582,4 +582,4 @@ def chunk_download(
|
|||||||
chunk = get_object_or_404(collection.chunks, uid=chunk_uid)
|
chunk = get_object_or_404(collection.chunks, uid=chunk_uid)
|
||||||
|
|
||||||
filename = chunk.chunkFile.path
|
filename = chunk.chunkFile.path
|
||||||
return FileResponse(filename, media_type="application/octet-stream")
|
return sendfile(filename)
|
||||||
|
9
etebase_fastapi/sendfile/backends/mod_wsgi.py
Normal file
9
etebase_fastapi/sendfile/backends/mod_wsgi.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from fastapi import Response
|
||||||
|
|
||||||
|
from ..utils import _convert_file_to_url
|
||||||
|
|
||||||
|
|
||||||
|
def sendfile(filename, **kwargs):
|
||||||
|
return Response(headers={"Location": _convert_file_to_url(filename)})
|
9
etebase_fastapi/sendfile/backends/nginx.py
Normal file
9
etebase_fastapi/sendfile/backends/nginx.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from fastapi import Response
|
||||||
|
|
||||||
|
from ..utils import _convert_file_to_url
|
||||||
|
|
||||||
|
|
||||||
|
def sendfile(filename, **kwargs):
|
||||||
|
return Response(headers={"X-Accel-Redirect": _convert_file_to_url(filename)})
|
12
etebase_fastapi/sendfile/backends/simple.py
Normal file
12
etebase_fastapi/sendfile/backends/simple.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from fastapi.responses import FileResponse
|
||||||
|
|
||||||
|
|
||||||
|
def sendfile(filename, mimetype, **kwargs):
|
||||||
|
"""Use the SENDFILE_ROOT value composed with the path arrived as argument
|
||||||
|
to build an absolute path with which resolve and return the file contents.
|
||||||
|
|
||||||
|
If the path points to a file out of the root directory (should cover both
|
||||||
|
situations with '..' and symlinks) then a 404 is raised.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return FileResponse(filename, media_type=mimetype)
|
6
etebase_fastapi/sendfile/backends/xsendfile.py
Normal file
6
etebase_fastapi/sendfile/backends/xsendfile.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from fastapi import Response
|
||||||
|
|
||||||
|
|
||||||
|
def sendfile(filename, **kwargs):
|
||||||
|
filename = str(filename)
|
||||||
|
return Response(headers={"X-Sendfile": filename})
|
@ -4,9 +4,11 @@ from pathlib import Path, PurePath
|
|||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from fastapi import status
|
||||||
|
from ..exceptions import HttpError
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.http import Http404
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -54,12 +56,12 @@ def _sanitize_path(filepath):
|
|||||||
try:
|
try:
|
||||||
filepath_abs.relative_to(path_root)
|
filepath_abs.relative_to(path_root)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise Http404("{} wrt {} is impossible".format(filepath_abs, path_root))
|
raise HttpError("generic", "{} wrt {} is impossible".format(filepath_abs, path_root), status_code=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
return filepath_abs
|
return filepath_abs
|
||||||
|
|
||||||
|
|
||||||
def sendfile(request, filename, mimetype="application/octet-stream", encoding=None):
|
def sendfile(filename, mimetype="application/octet-stream", encoding=None):
|
||||||
"""
|
"""
|
||||||
Create a response to send file using backend configured in ``SENDFILE_BACKEND``
|
Create a response to send file using backend configured in ``SENDFILE_BACKEND``
|
||||||
|
|
||||||
@ -75,11 +77,10 @@ def sendfile(request, filename, mimetype="application/octet-stream", encoding=No
|
|||||||
_sendfile = _get_sendfile()
|
_sendfile = _get_sendfile()
|
||||||
|
|
||||||
if not filepath_obj.exists():
|
if not filepath_obj.exists():
|
||||||
raise Http404('"%s" does not exist' % filepath_obj)
|
raise HttpError("does_not_exist", '"%s" does not exist' % filepath_obj, status_code=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
response = _sendfile(request, filepath_obj, mimetype=mimetype)
|
response = _sendfile(filepath_obj, mimetype=mimetype)
|
||||||
|
|
||||||
response["Content-length"] = filepath_obj.stat().st_size
|
response.headers["Content-Type"] = mimetype
|
||||||
response["Content-Type"] = mimetype
|
|
||||||
|
|
||||||
return response
|
return response
|
Loading…
Reference in New Issue
Block a user