Pārlūkot izejas kodu

only return content-range if range is requested
also adding support for E-Tag and Last-Modified

Rodja Trappe 1 gadu atpakaļ
vecāks
revīzija
2453a90f8b
2 mainītis faili ar 31 papildinājumiem un 14 dzēšanām
  1. 1 1
      nicegui/app/app.py
  2. 30 13
      nicegui/app/streaming_response.py

+ 1 - 1
nicegui/app/app.py

@@ -226,7 +226,7 @@ class App(FastAPI):
         path = f'/_nicegui/auto/media/{helpers.hash_file_path(file)}/{file.name}' if url_path is None else url_path
 
         @self.get(path)
-        def read_item(request: Request, nicegui_cunk_size: int = 8192) -> StreamingResponse:
+        def read_item(request: Request, nicegui_cunk_size: int = 8192) -> Response:
             if single_use:
                 self.remove_route(path)
             return get_streaming_response(file, request, chunk_size=nicegui_cunk_size)

+ 30 - 13
nicegui/app/streaming_response.py

@@ -1,30 +1,48 @@
+import hashlib
 import mimetypes
+from datetime import datetime
 from pathlib import Path
 from typing import Generator
 
 from fastapi import Request
-from fastapi.responses import StreamingResponse
+from fastapi.responses import Response, StreamingResponse
 
 mimetypes.init()
 
 
-def get_streaming_response(file: Path, request: Request, chunk_size: int) -> StreamingResponse:
-    """Get a StreamingResponse for the given file and request."""
+def get_streaming_response(file: Path, request: Request, chunk_size: int) -> Response:
+    """Get a Response for the given file, supporting range-requests, E-Tag and Last-Modified."""
     file_size = file.stat().st_size
+    last_modified_time = datetime.utcfromtimestamp(file.stat().st_mtime)
     start = 0
     end = file_size - 1
+    e_tag = hashlib.md5((str(last_modified_time) + str(file_size)).encode()).hexdigest()
+    if_match_header = request.headers.get('If-None-Match')
+    if if_match_header and if_match_header == e_tag:
+        return Response(status_code=304)  # Not Modified
+    headers = {
+        'E-Tag': e_tag,
+        'Last-Modified': last_modified_time.strftime("%a, %d %b %Y %H:%M:%S GMT")
+    }
     range_header = request.headers.get('Range')
-    if range_header:
-        byte1, byte2 = range_header.split('=')[1].split('-')
-        start = int(byte1)
-        if byte2:
-            end = int(byte2)
+    media_type = mimetypes.guess_type(str(file))[0] or 'application/octet-stream'
+    if not range_header:
+        return StreamingResponse(
+            file.open('rb'),
+            media_type=media_type,
+            headers=headers,
+            status_code=200,
+        )
+    byte1, byte2 = range_header.split('=')[1].split('-')
+    start = int(byte1)
+    if byte2:
+        end = int(byte2)
     content_length = end - start + 1
-    headers = {
-        'Content-Range': f'bytes {start}-{end}/{file_size}',
+    headers.update({
         'Content-Length': str(content_length),
+        'Content-Range': f'bytes {start}-{end}/{file_size}',
         'Accept-Ranges': 'bytes',
-    }
+    })
 
     def content_reader(file: Path, start: int, end: int) -> Generator[bytes, None, None]:
         with open(file, 'rb') as data:
@@ -36,10 +54,9 @@ def get_streaming_response(file: Path, request: Request, chunk_size: int) -> Str
                     break
                 yield chunk
                 remaining_bytes -= len(chunk)
-
     return StreamingResponse(
         content_reader(file, start, end),
-        media_type=mimetypes.guess_type(str(file))[0] or 'application/octet-stream',
+        media_type=media_type,
         headers=headers,
         status_code=206,
     )