-
-
Notifications
You must be signed in to change notification settings - Fork 527
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Simon Høxbro Hansen <[email protected]>
- Loading branch information
1 parent
0ca56c1
commit 55f71a9
Showing
9 changed files
with
625 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import io\n", | ||
"import panel as pn\n", | ||
"pn.extension('filedropper')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The `FileDropper` allows the user to upload one or more files to the server. It is built on top of the [FilePond](https://pqina.nl/filepond/) library, if you use this component extensively consider donating to them. The `FileDropper` is similar to the `FileInput` widget but additionally adds support for chunked uploads, making it possible to upload large files. The UI also supports previews for image files. Unlike `FileInput` the uploaded files are stored as dictionary of bytes object indexed by the filename.\n", | ||
"\n", | ||
"Discover more on using widgets to add interactivity to your applications in the [how-to guides on interactivity](../how_to/interactivity/index.md). Alternatively, learn [how to set up callbacks and (JS-)links between parameters](../../how_to/links/index.md) or [how to use them as part of declarative UIs with Param](../../how_to/param/index.html).\n", | ||
"\n", | ||
"#### Parameters:\n", | ||
"\n", | ||
"For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n", | ||
"\n", | ||
"##### Core\n", | ||
"\n", | ||
"* **`accepted_filetypes`** (list): List of accepted file types. Can be mimetypes, file extensions or wild cards. For instance `['image/*']` will accept all images while `['.png', 'image/jpeg']` will only accepts PNGs and JPEGs.\n", | ||
"* **`chunk_size`** (int): Size in bytes per chunk transferred across the WebSocket (`default=10000000`, i.e. 10MB).\n", | ||
"* **`layout`** (Literal[\"circle\", \"compact\", \"integrated\"] | None): Compact mode will remove padding, integrated mode is used to render FilePond as part of a bigger element (and should not be used with `multiple=True`. Circle mode adjusts the item position offsets so buttons and progress indicators don't fall outside of the circular shape.\n", | ||
"* **`max_file_size`** (str): Maximum size of a file as a string with units given in KB or MB, e.g. 5MB or 750KB.\n", | ||
"* **`max_files`** (int): Maximum number of files that can be uploaded if `multiple=True`.\n", | ||
"* **`max_total_file_size`** (str): Maximum size of all uploaded files, as a string with units given in KB or MB, e.g. 5MB or 750KB.\n", | ||
"* **`mime_type`** (dict[str, str]): A dictionary containing the mimetypes for each of the uploaded files indexed by their filename.\n", | ||
"* **`multiple`** (bool): Whether to allow uploading multiple files.\n", | ||
"* **`value`** (dict[str, str | bytes]): A dictionary containing the uploaded file(s) as bytes or string objects indexed by the filename. Files that have a `text/*` mimetype will automatically be decoded as `utf-8`.\n", | ||
"\n", | ||
"___" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"file_dropper = pn.widgets.FileDropper()\n", | ||
"\n", | ||
"file_dropper" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Try uploading an image or PDF file and you will see a preview of the uploaded file.\n", | ||
"\n", | ||
"To read out the content of the file you can access the ``value`` parameter, which holds a dictionary mapping from the filename to a string or [bytestring](https://docs.python.org/3/library/stdtypes.html#bytes-objects) containing the file's contents. Any filetype that declares a `text/*` mimetype will automatically be decoded into a string. The mimetype itself is made available on the `mime_type` parameter expressed as a MIME type, e.g. `image/png` or `text/csv`, again expressed as a dictionary mapping from filename to filetype." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"file_dropper.value" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Filetypes\n", | ||
"\n", | ||
"The `accepted_filetypes` parameter restricts what files the user can pick from. This consists of a list of mimetypes that also allows wildcards. Values can be:\n", | ||
"\n", | ||
"* `<file extension>` - Specific file extension(s) (e.g: .gif, .jpg, .png, .doc) are pickable\n", | ||
"* `audio/*` - all sound files are pickable\n", | ||
"* `video/*` - all video files are pickable\n", | ||
"* `image/*` - all image files are pickable\n", | ||
"* `<media type>` - A valid [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml), with no parameters." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"file_dropper = pn.widgets.FileDropper(accepted_filetypes=['.png', 'image/jpeg'])\n", | ||
"\n", | ||
"file_dropper" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"To allow uploading multiple files we can also set `multiple=True`:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"file_dropper = pn.widgets.FileInput(multiple=True)\n", | ||
"\n", | ||
"file_dropper" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Layout\n", | ||
"\n", | ||
"The `FileDropper` allows for a few different layout options:\n", | ||
"\n", | ||
"- `\"compact\"`: Remove margins.\n", | ||
"- `\"integrated\"`: Removes background and other styling. Useful when the component is embedded inside a larger component.\n", | ||
"- `\"circle\"`: Circular upload area useful for profile picture uploads." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"pn.Row(\n", | ||
" pn.widgets.FileDropper(layout=\"compact\"),\n", | ||
" pn.widgets.FileDropper(layout=\"integrated\", styles={'background-color': 'black', 'border-radius': '1em', 'color': 'white'}),\n", | ||
" pn.widgets.FileDropper(layout=\"circle\"),\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Upload size limits\n", | ||
"\n", | ||
"Unlike the `FileInput` widget the `FileDropper` widget bypasses restrictions to the maximum file size imposed by web browsers, Bokeh, Tornado, notebooks, etc. by chunking large uploads. This makes it feasible to upload much larger files than would otherwise be possible. The default `chunk_size` is 10MB (which is expressed in as 10000000 bytes). Even if it is possible to increase this limit by setting some parameters (described below), bear in mind that the `FileInput` widget is not meant to upload large files. You can configure `max_file_size`, `max_total_file_size` (limiting the total upload size if you have set `multiple=True`) and `max_files` to provide an upper bound on the amount of data that can be uploaded." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Controls\n", | ||
"\n", | ||
"The `FileInput` widget exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"pn.Row(file_dropper.controls(jslink=True), file_dropper)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"language_info": { | ||
"name": "python", | ||
"pygments_lexer": "ipython3" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 4 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
from bokeh.core.properties import ( | ||
Bool, Dict, Enum, Int, List, Nullable, String, | ||
) | ||
from bokeh.events import ModelEvent | ||
from bokeh.models.widgets import InputWidget | ||
|
||
from ..config import config | ||
from ..io.resources import bundled_files | ||
from ..util import classproperty | ||
|
||
|
||
class UploadEvent(ModelEvent): | ||
|
||
event_name = 'upload_event' | ||
|
||
def __init__(self, model, data=None): | ||
self.data = data | ||
super().__init__(model=model) | ||
|
||
class DeleteEvent(ModelEvent): | ||
|
||
event_name = 'delete_event' | ||
|
||
def __init__(self, model, data=None): | ||
self.data = data | ||
super().__init__(model=model) | ||
|
||
|
||
class FileDropper(InputWidget): | ||
|
||
accepted_filetypes = List(String) | ||
|
||
chunk_size = Int(10_000_000) | ||
|
||
max_files = Nullable(Int) | ||
|
||
max_file_size = Nullable(String) | ||
|
||
max_total_file_size = Nullable(String) | ||
|
||
mime_type = Dict(String, String) | ||
|
||
multiple = Bool(True) | ||
|
||
layout = Nullable(Enum("integrated", "compact", "circle", default="compact")) | ||
|
||
__javascript_raw__ = [ | ||
f"{config.npm_cdn}/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js", | ||
f"{config.npm_cdn}/filepond-plugin-file-validate-size/dist/filepond-plugin-file-validate-size.js", | ||
f"{config.npm_cdn}/filepond-plugin-file-validate-type/dist/filepond-plugin-file-validate-type.js", | ||
f"{config.npm_cdn}/filepond-plugin-pdf-preview/dist/filepond-plugin-pdf-preview.min.js", | ||
f"{config.npm_cdn}/filepond@^4/dist/filepond.js" | ||
] | ||
|
||
@classproperty | ||
def __javascript__(cls): | ||
return bundled_files(cls) | ||
|
||
__css_raw__ = [ | ||
f"{config.npm_cdn}/filepond@^4/dist/filepond.css", | ||
f"{config.npm_cdn}/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css", | ||
f"{config.npm_cdn}/filepond-plugin-pdf-preview/dist/filepond-plugin-pdf-preview.css" | ||
] | ||
|
||
@classproperty | ||
def __css__(cls): | ||
return bundled_files(cls, 'css') |
Oops, something went wrong.