Explorar el Código

Merge pull request #2050 from Skilles/main

Allow client to download raw data from server
Falko Schindler hace 1 año
padre
commit
995c886b84
Se han modificado 4 ficheros con 26 adiciones y 13 borrados
  1. 3 3
      nicegui/client.py
  2. 8 7
      nicegui/functions/download.py
  3. 6 3
      nicegui/templates/index.html
  4. 9 0
      tests/test_download.py

+ 3 - 3
nicegui/client.py

@@ -192,9 +192,9 @@ class Client:
         path = target if isinstance(target, str) else self.page_routes[target]
         outbox.enqueue_message('open', {'path': path, 'new_tab': new_tab}, self.id)
 
-    def download(self, url: str, filename: Optional[str] = None) -> None:
-        """Download a file from the given URL."""
-        outbox.enqueue_message('download', {'url': url, 'filename': filename}, self.id)
+    def download(self, src: Union[str, bytes], filename: Optional[str] = None) -> None:
+        """Download a file from a given URL or raw bytes."""
+        outbox.enqueue_message('download', {'src': src, 'filename': filename}, self.id)
 
     def on_connect(self, handler: Union[Callable[..., Any], Awaitable]) -> None:
         """Register a callback to be called when the client connects."""

+ 8 - 7
nicegui/functions/download.py

@@ -4,16 +4,17 @@ from typing import Optional, Union
 from .. import context, core, helpers
 
 
-def download(src: Union[str, Path], filename: Optional[str] = None) -> None:
+def download(src: Union[str, Path, bytes], filename: Optional[str] = None) -> None:
     """Download
 
-    Function to trigger the download of a file.
+    Function to trigger the download of a file, URL or bytes.
 
-    :param src: target URL or local path of the file which should be downloaded
+    :param src: target URL, local path of a file or raw data which should be downloaded
     :param filename: name of the file to download (default: name of the file on the server)
     """
-    if helpers.is_file(src):
-        src = core.app.add_static_file(local_file=src, single_use=True)
-    else:
-        src = str(src)
+    if not isinstance(src, bytes):
+        if helpers.is_file(src):
+            src = core.app.add_static_file(local_file=src, single_use=True)
+        else:
+            src = str(src)
     context.get_client().download(src, filename)

+ 6 - 3
nicegui/templates/index.html

@@ -208,14 +208,17 @@
         });
       }
 
-      function download(url, filename) {
+      function download(src, filename) {
         const anchor = document.createElement("a");
-        anchor.href = url;
+        anchor.href = typeof src === "string" ? src : URL.createObjectURL(new Blob([src]));
         anchor.target = "_blank";
         anchor.download = filename || "";
         document.body.appendChild(anchor);
         anchor.click();
         document.body.removeChild(anchor);
+        if (typeof src !== "string") {
+          URL.revokeObjectURL(url);
+        }
       }
 
       async function loadDependencies(element) {
@@ -297,7 +300,7 @@
               const target = msg.new_tab ? '_blank' : '_self';
               window.open(url, target);
             },
-            download: (msg) => download(msg.url, msg.filename),
+            download: (msg) => download(msg.src, msg.filename),
             notify: (msg) => Quasar.Notify.create(msg),
           };
           const socketMessageQueue = [];

+ 9 - 0
tests/test_download.py

@@ -40,3 +40,12 @@ def test_downloading_local_file_as_src(screen: Screen):
     screen.wait(0.5)
     assert (DOWNLOAD_DIR / 'slide1.jpg').exists()
     assert len(app.routes) == route_count_before_download
+
+
+def test_download_raw_data(screen: Screen):
+    ui.button('download', on_click=lambda: ui.download(b'test', 'test.txt'))
+
+    screen.open('/')
+    screen.click('download')
+    screen.wait(0.5)
+    assert (DOWNLOAD_DIR / 'test.txt').read_text() == 'test'