Feature/Multi File upload (#712)
* This PR adds a feature to upload multiple files * modified function to support python 3.8 * change code to use types instead of arg name
This commit is contained in:
parent
25472d7d85
commit
a2f86f9fbb
@ -192,6 +192,7 @@ export const connect = async (
|
||||
* @param setResult The function to set the result.
|
||||
* @param files The files to upload.
|
||||
* @param handler The handler to use.
|
||||
* @param multiUpload Whether handler args on backend is multiupload
|
||||
* @param endpoint The endpoint to upload to.
|
||||
*/
|
||||
export const uploadFiles = async (
|
||||
@ -200,6 +201,7 @@ export const uploadFiles = async (
|
||||
setResult,
|
||||
files,
|
||||
handler,
|
||||
multiUpload,
|
||||
endpoint
|
||||
) => {
|
||||
// If we are already processing an event, or there are no upload files, return.
|
||||
@ -210,15 +212,16 @@ export const uploadFiles = async (
|
||||
// Set processing to true to block other events from being processed.
|
||||
setResult({ ...result, processing: true });
|
||||
|
||||
// Currently only supports uploading one file.
|
||||
const file = files[0];
|
||||
const name = multiUpload ? "files" : "file"
|
||||
const headers = {
|
||||
"Content-Type": file.type,
|
||||
"Content-Type": files[0].type,
|
||||
};
|
||||
const formdata = new FormData();
|
||||
|
||||
// Add the token and handler to the file name.
|
||||
formdata.append("file", file, getToken() + ":" + handler + ":" + file.name);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
formdata.append("files", files[i], getToken() + ":" + handler + ":" + name + ":" + files[i].name);
|
||||
}
|
||||
|
||||
// Send the file to the server.
|
||||
await axios.post(endpoint, formdata, headers).then((response) => {
|
||||
|
@ -465,22 +465,27 @@ def upload(app: App):
|
||||
The upload function.
|
||||
"""
|
||||
|
||||
async def upload_file(file: UploadFile):
|
||||
async def upload_file(files: List[UploadFile]):
|
||||
"""Upload a file.
|
||||
|
||||
Args:
|
||||
file: The file to upload.
|
||||
files: The file(s) to upload.
|
||||
|
||||
Returns:
|
||||
The state update after processing the event.
|
||||
"""
|
||||
# Get the token and filename.
|
||||
token, handler, filename = file.filename.split(":", 2)
|
||||
file.filename = filename
|
||||
token, handler, key = files[0].filename.split(":")[:3]
|
||||
for file in files:
|
||||
file.filename = file.filename.split(":")[-1]
|
||||
|
||||
# Get the state for the session.
|
||||
state = app.state_manager.get_state(token)
|
||||
event = Event(token=token, name=handler, payload={"file": file})
|
||||
# Event payload should have `files` as key for multi-uploads and `file` otherwise
|
||||
event = Event(
|
||||
token=token,
|
||||
name=handler,
|
||||
payload={key: files[0] if key == "file" else files},
|
||||
)
|
||||
update = await state.process(event)
|
||||
return update
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@ -298,9 +299,15 @@ def format_upload_event(event_spec: EventSpec) -> str:
|
||||
"""
|
||||
from pynecone.compiler import templates
|
||||
|
||||
multi_upload = any(
|
||||
types._issubclass(arg_type, List)
|
||||
for arg_type in inspect.getfullargspec(
|
||||
event_spec.handler.fn
|
||||
).annotations.values()
|
||||
)
|
||||
state, name = get_event_handler_parts(event_spec.handler)
|
||||
parent_state = state.split(".")[0]
|
||||
return f'uploadFiles({parent_state}, {templates.RESULT}, {templates.SET_RESULT}, {parent_state}.files, "{state}.{name}", UPLOAD)'
|
||||
return f'uploadFiles({parent_state}, {templates.RESULT}, {templates.SET_RESULT}, {parent_state}.files, "{state}.{name}",{str(multi_upload).lower()} ,UPLOAD)'
|
||||
|
||||
|
||||
def format_query_params(router_data: Dict[str, Any]) -> Dict[str, str]:
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Test fixtures."""
|
||||
import platform
|
||||
from typing import Generator
|
||||
from typing import Generator, List
|
||||
|
||||
import pytest
|
||||
|
||||
@ -141,6 +141,7 @@ class UploadState(pc.State):
|
||||
"""The base state for uploading a file."""
|
||||
|
||||
img: str
|
||||
img_list: List[str]
|
||||
|
||||
async def handle_upload(self, file: pc.UploadFile):
|
||||
"""Handle the upload of a file.
|
||||
@ -158,6 +159,23 @@ class UploadState(pc.State):
|
||||
# Update the img var.
|
||||
self.img = file.filename
|
||||
|
||||
async def multi_handle_upload(self, files: List[pc.UploadFile]):
|
||||
"""Handle the upload of a file.
|
||||
|
||||
Args:
|
||||
files: The uploaded files.
|
||||
"""
|
||||
for file in files:
|
||||
upload_data = await file.read()
|
||||
outfile = f".web/public/{file.filename}"
|
||||
|
||||
# Save the file.
|
||||
with open(outfile, "wb") as file_object:
|
||||
file_object.write(upload_data)
|
||||
|
||||
# Update the img var.
|
||||
self.img_list.append(file.filename)
|
||||
|
||||
|
||||
class BaseState(pc.State):
|
||||
"""The test base state."""
|
||||
@ -197,3 +215,13 @@ def upload_sub_state_event_spec():
|
||||
Event Spec.
|
||||
"""
|
||||
return EventSpec(handler=SubUploadState.handle_upload, upload=True) # type: ignore
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multi_upload_event_spec():
|
||||
"""Create an event Spec for a multi-upload base state.
|
||||
|
||||
Returns:
|
||||
Event Spec.
|
||||
"""
|
||||
return EventSpec(handler=UploadState.multi_handle_upload, upload=True) # type: ignore
|
||||
|
@ -296,7 +296,7 @@ def test_format_upload_event(upload_event_spec):
|
||||
assert (
|
||||
format.format_upload_event(upload_event_spec)
|
||||
== "uploadFiles(upload_state, result, setResult, "
|
||||
'upload_state.files, "upload_state.handle_upload", '
|
||||
'upload_state.files, "upload_state.handle_upload",false ,'
|
||||
"UPLOAD)"
|
||||
)
|
||||
|
||||
@ -310,5 +310,19 @@ def test_format_sub_state_event(upload_sub_state_event_spec):
|
||||
assert (
|
||||
format.format_upload_event(upload_sub_state_event_spec)
|
||||
== "uploadFiles(base_state, result, setResult, base_state.files, "
|
||||
'"base_state.sub_upload_state.handle_upload", UPLOAD)'
|
||||
'"base_state.sub_upload_state.handle_upload",false ,UPLOAD)'
|
||||
)
|
||||
|
||||
|
||||
def test_format_multi_upload_event(multi_upload_event_spec):
|
||||
"""Test formatting an upload event spec.
|
||||
|
||||
Args:
|
||||
multi_upload_event_spec: The event spec fixture.
|
||||
"""
|
||||
assert (
|
||||
format.format_upload_event(multi_upload_event_spec)
|
||||
== "uploadFiles(upload_state, result, setResult, "
|
||||
'upload_state.files, "upload_state.multi_handle_upload",true ,'
|
||||
"UPLOAD)"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user