Compare commits
8 Commits
main
...
lendemor/f
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3a139b40ed | ||
![]() |
802ed6df71 | ||
![]() |
c28830f9eb | ||
![]() |
2d8666ae58 | ||
![]() |
2b5277fb98 | ||
![]() |
60798ed1a1 | ||
![]() |
5b283baef4 | ||
![]() |
7a97cf3672 |
@ -159,13 +159,34 @@ export const applyEvent = async (event, socket) => {
|
||||
}
|
||||
|
||||
if (event.name == "_download") {
|
||||
const a = document.createElement("a");
|
||||
a.hidden = true;
|
||||
// Special case when linking to uploaded files
|
||||
a.href = event.payload.url.replace("${getBackendURL(env.UPLOAD)}", getBackendURL(env.UPLOAD))
|
||||
a.download = event.payload.filename;
|
||||
a.click();
|
||||
a.remove();
|
||||
const getFilenameFromUrl = (url) => url.split("/").pop().split("?")[0];
|
||||
|
||||
// if the URL come from an upload handler, replace the backend URL placeholder with the actual backend URL
|
||||
const downloadUrl = event.payload.url.replace(
|
||||
"${getBackendURL(env.UPLOAD)}",
|
||||
getBackendURL(env.UPLOAD)
|
||||
);
|
||||
const filename = event.payload.filename || getFilenameFromUrl(downloadUrl);
|
||||
|
||||
fetch(downloadUrl, {
|
||||
method: "GET",
|
||||
headers: { "X-Filename": filename },
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok)
|
||||
throw new Error(`Download of file at ${downloadUrl} failed`);
|
||||
return response.blob();
|
||||
})
|
||||
.then((blob) => {
|
||||
const blobURL = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
|
||||
a.href = blobURL;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(blobURL);
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -647,11 +668,11 @@ export const useEventLoop = (
|
||||
// Route after the initial page hydration.
|
||||
useEffect(() => {
|
||||
const change_start = () => {
|
||||
const main_state_dispatch = dispatch["state"]
|
||||
const main_state_dispatch = dispatch["state"];
|
||||
if (main_state_dispatch !== undefined) {
|
||||
main_state_dispatch({is_hydrated: false})
|
||||
main_state_dispatch({ is_hydrated: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
const change_complete = () => addEvents(onLoadInternalEvent());
|
||||
router.events.on("routeChangeStart", change_start);
|
||||
router.events.on("routeChangeComplete", change_complete);
|
||||
|
@ -29,7 +29,6 @@ from typing import (
|
||||
from fastapi import FastAPI, HTTPException, Request, UploadFile
|
||||
from fastapi.middleware import cors
|
||||
from fastapi.responses import StreamingResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
||||
from socketio import ASGIApp, AsyncNamespace, AsyncServer
|
||||
from starlette_admin.contrib.sqla.admin import Admin
|
||||
@ -77,6 +76,7 @@ from reflex.state import (
|
||||
_substate_key,
|
||||
code_uses_state_contexts,
|
||||
)
|
||||
from reflex.staticfiles import UploadedFiles
|
||||
from reflex.utils import console, exceptions, format, prerequisites, types
|
||||
from reflex.utils.exec import is_testing_env, should_skip_compile
|
||||
from reflex.utils.imports import ImportVar
|
||||
@ -279,7 +279,7 @@ class App(Base):
|
||||
# To access uploaded files.
|
||||
self.api.mount(
|
||||
str(constants.Endpoint.UPLOAD),
|
||||
StaticFiles(directory=get_upload_dir()),
|
||||
UploadedFiles(directory=get_upload_dir()),
|
||||
name="uploaded_files",
|
||||
)
|
||||
|
||||
|
@ -140,7 +140,9 @@ def get_upload_url(file_path: str) -> Var[str]:
|
||||
"""
|
||||
Upload.is_used = True
|
||||
|
||||
return Var.create_safe(f"{uploaded_files_url_prefix}/{file_path}")
|
||||
return Var.create_safe(
|
||||
f"{uploaded_files_url_prefix}/{file_path}", _var_is_string=True
|
||||
)
|
||||
|
||||
|
||||
def _on_drop_spec(files: Var):
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Event-related constants."""
|
||||
|
||||
from enum import Enum
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
30
reflex/staticfiles.py
Normal file
30
reflex/staticfiles.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""Classes for staticfiles served by Reflex backend."""
|
||||
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
from starlette.types import Scope
|
||||
|
||||
|
||||
class UploadedFiles(StaticFiles):
|
||||
"""Static files with download headers."""
|
||||
|
||||
async def get_response(self, path: str, scope: Scope) -> Response:
|
||||
"""Get the response for a static file with download headers.
|
||||
|
||||
Args:
|
||||
path: The path of the static file.
|
||||
scope: The request scope.
|
||||
|
||||
Returns:
|
||||
The response for the static file with download headers.
|
||||
"""
|
||||
req = Request(scope)
|
||||
if "x-filename" in req.headers:
|
||||
filename = req.headers["x-filename"]
|
||||
content_disposition = f'attachment; filename="{filename}"'
|
||||
else:
|
||||
content_disposition = "attachment"
|
||||
response = await super().get_response(path, scope)
|
||||
response.headers["Content-Disposition"] = content_disposition
|
||||
return response
|
Loading…
Reference in New Issue
Block a user