Skip to content

Commit f76c780

Browse files
maximltphilippjfr
andauthored
Ensure Filedropper.accepted_filetypes supports most extension types (#8380)
* convert extension to mime type * extend the mapping * Apply suggestion from @philippjfr --------- Co-authored-by: Philipp Rudiger <prudiger@anaconda.com>
1 parent e7368ad commit f76c780

1 file changed

Lines changed: 157 additions & 3 deletions

File tree

panel/models/file_dropper.ts

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,167 @@ export class DeleteEvent extends ModelEvent {
3737
}
3838
}
3939

40+
// Mapping of file extensions to browser-reported MIME types
41+
// as it's not yet supported by filepond, see:
42+
// https://github.com/pqina/filepond-plugin-file-validate-type/issues/13
43+
const EXTENSION_TO_MIME_TYPE: Record<string, string> = {
44+
// Images
45+
".png": "image/png",
46+
".jpg": "image/jpeg",
47+
".jpeg": "image/jpeg",
48+
".gif": "image/gif",
49+
".svg": "image/svg+xml",
50+
".webp": "image/webp",
51+
".bmp": "image/bmp",
52+
".ico": "image/x-icon",
53+
".tiff": "image/tiff",
54+
".tif": "image/tiff",
55+
".avif": "image/avif",
56+
57+
// Video
58+
".mp4": "video/mp4",
59+
".webm": "video/webm",
60+
".mov": "video/quicktime",
61+
".mkv": "video/x-matroska",
62+
".avi": "video/x-msvideo",
63+
".wmv": "video/x-ms-wmv",
64+
".flv": "video/x-flv",
65+
66+
// Audio
67+
".mp3": "audio/mpeg",
68+
".wav": "audio/wav",
69+
".ogg": "audio/ogg",
70+
".oga": "audio/ogg",
71+
".m4a": "audio/mp4",
72+
".aac": "audio/aac",
73+
".flac": "audio/flac",
74+
75+
// Text / documents
76+
".txt": "text/plain",
77+
".md": "text/markdown",
78+
".rtf": "application/rtf",
79+
".pdf": "application/pdf",
80+
81+
// Structured data (browser-compatible)
82+
".json": "application/json",
83+
".xml": "text/xml",
84+
".csv": "text/csv",
85+
".tsv": "text/tab-separated-values",
86+
".yaml": "text/yaml",
87+
".yml": "text/yaml",
88+
89+
// Web / code
90+
".html": "text/html",
91+
".htm": "text/html",
92+
".css": "text/css",
93+
".js": "text/javascript",
94+
".mjs": "text/javascript",
95+
".ts": "application/typescript",
96+
".jsx": "text/jsx",
97+
".tsx": "text/tsx",
98+
".wasm": "application/wasm",
99+
100+
// Office documents
101+
".doc": "application/msword",
102+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
103+
".xls": "application/vnd.ms-excel",
104+
".xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12",
105+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
106+
".ppt": "application/vnd.ms-powerpoint",
107+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
108+
".odt": "application/vnd.oasis.opendocument.text",
109+
".ods": "application/vnd.oasis.opendocument.spreadsheet",
110+
".odp": "application/vnd.oasis.opendocument.presentation",
111+
112+
// Archives
113+
".zip": "application/zip",
114+
".tar": "application/x-tar",
115+
".gz": "application/gzip",
116+
".tgz": "application/gzip",
117+
".bz2": "application/x-bzip2",
118+
".xz": "application/x-xz",
119+
".7z": "application/x-7z-compressed",
120+
".rar": "application/vnd.rar",
121+
122+
// Fonts
123+
".ttf": "font/ttf",
124+
".otf": "font/otf",
125+
".woff": "font/woff",
126+
".woff2": "font/woff2",
127+
128+
// Executables / binaries
129+
".exe": "application/vnd.microsoft.portable-executable",
130+
".dll": "application/vnd.microsoft.portable-executable",
131+
".bin": "application/octet-stream",
132+
".dmg": "application/x-apple-diskimage",
133+
".pkg": "application/octet-stream",
134+
".deb": "application/vnd.debian.binary-package",
135+
".rpm": "application/x-rpm",
136+
137+
// Columnar / big data
138+
".parquet": "application/octet-stream", // browsers do NOT emit application/parquet
139+
".arrow": "application/octet-stream",
140+
".feather": "application/octet-stream",
141+
".orc": "application/octet-stream",
142+
143+
// Scientific / numerical
144+
".npy": "application/octet-stream",
145+
".npz": "application/octet-stream",
146+
".mat": "application/octet-stream",
147+
".h5": "application/octet-stream",
148+
".hdf5": "application/octet-stream",
149+
".zarr": "application/octet-stream",
150+
151+
// R ecosystem
152+
".rds": "application/octet-stream",
153+
".rda": "application/octet-stream",
154+
".rdata": "application/octet-stream",
155+
156+
// Notebooks
157+
".ipynb": "application/json",
158+
159+
// ML models / artifacts
160+
".pkl": "application/octet-stream",
161+
".pickle": "application/octet-stream",
162+
".joblib": "application/octet-stream",
163+
".onnx": "application/octet-stream",
164+
".pt": "application/octet-stream",
165+
".pth": "application/octet-stream",
166+
".h5model": "application/octet-stream",
167+
168+
// Vector
169+
".shp": "application/octet-stream",
170+
".shx": "application/octet-stream",
171+
".dbf": "application/octet-stream",
172+
".geojson": "application/geo+json",
173+
".gpkg": "application/octet-stream",
174+
175+
// Raster
176+
".geotiff": "image/tiff",
177+
".asc": "text/plain",
178+
}
179+
40180
export class FileDropperView extends InputWidgetView {
41181
declare model: FileDropper
42182
declare input_el: HTMLInputElement
43183
_file_pond: any | null = null
44184
_transfer_in_process: string | null = null
45185

186+
private extensionsToMimeTypes(types: string[]): string[] {
187+
return types.map(type => {
188+
// MIME types, including wildcards like "image/*"
189+
if (type.includes("/")) {
190+
return type
191+
}
192+
// Guessing it's an extension
193+
if (type.startsWith(".")) {
194+
const lowerType = type.toLowerCase()
195+
return EXTENSION_TO_MIME_TYPE[lowerType] || type
196+
}
197+
return type
198+
})
199+
}
200+
46201
override initialize(): void {
47202
super.initialize()
48203
const {previews} = this.model
@@ -61,7 +216,7 @@ export class FileDropperView extends InputWidgetView {
61216
const {disabled, layout, max_file_size, max_files, max_total_file_size, multiple} = this.model.properties
62217
this.on_change([disabled, max_file_size, max_files, max_total_file_size, multiple, layout], () => {
63218
this._file_pond?.setOptions({
64-
acceptedFileTypes: this.model.accepted_filetypes,
219+
acceptedFileTypes: this.extensionsToMimeTypes(this.model.accepted_filetypes),
65220
allowMultiple: this.model.multiple,
66221
disabled: this.model.disabled,
67222
maxFiles: this.model.max_files,
@@ -91,9 +246,8 @@ export class FileDropperView extends InputWidgetView {
91246

92247
override render(): void {
93248
super.render()
94-
95249
this._file_pond = (window as any).FilePond.create(this.input_el, {
96-
acceptedFileTypes: this.model.accepted_filetypes,
250+
acceptedFileTypes: this.extensionsToMimeTypes(this.model.accepted_filetypes),
97251
allowMultiple: this.model.multiple,
98252
credits: false,
99253
disabled: this.model.disabled,

0 commit comments

Comments
 (0)