The upload door of your Gradio app: one file, a batch, or a whole directory -- with a bouncer (file_types) built in.
Your model is useless in the browser until someone can hand it a file. gr.File is that handoff -- and it ships with a bouncer.
gr.File renders a drag-and-drop upload zone and hands your function either a temp path (type='filepath', the default) or raw bytes (type='binary'). file_count='single' gives you one path str, 'multiple' a list of paths, 'directory' a folder pick. Every file your function sees is a copy in the system temp dir -- you never touch the user's original.
Almost every ML demo outside plain text starts here: transcribe this audio, summarize this PDF, ingest this CSV. file_types=['.pdf'] is both a frontend filter and a backend check -- the server rejects anything else with a gr.Error before your function runs, so you delete defensive code instead of writing it.
import gradio as gr
def summarize(path):
txt = open(path, encoding="utf-8", errors="replace").read()
words = len(txt.split())
first = txt.splitlines()[0][:60]
return f"{words} words, {len(txt)} chars -- first line: {first}"
demo = gr.Interface(
fn=summarize,
inputs=gr.File(label="Upload a .txt or .md", file_types=[".txt", ".md"]),
outputs=gr.Textbox(label="Summary"),
)
demo.launch()* Running on local URL: http://127.0.0.1:7860 Renders an Interface page: a gr.File drop zone labeled 'Upload a .txt or .md' (single-file mode, file picker filtered to .txt/.md) feeding a 'Run' button, feeding a Textbox labeled 'Summary'. Preprocess handed the function the temp path string '/tmp/tmppj68vo96.txt' for an uploaded 76-byte solar-plant notes file; the function returned: '11 words, 76 chars -- first line: Solar thermal: 92% efficiency, 40 MJ/m2/day.' Tested with gradio 6.18.0.
type='filepath' (the default) gives you a plain str temp path -- no .name attribute, no wrapper. You literally just open(path), as this example does.
import csv, gradio as gr
def merge_csvs(files):
rows, header = [], None
for p in files:
with open(p, newline="", encoding="utf-8") as f:
r = csv.reader(f)
header = header or next(r)
rows.extend(r)
return f"{len(rows)} data rows, columns seen: {header}"
demo = gr.Interface(
fn=merge_csvs,
inputs=gr.Files(label="Drop your CSV files"),
outputs=gr.Textbox(label="Merged preview"),
)
demo.launch()* Running on local URL: http://127.0.0.1:7860 Renders the same Interface shape as example 1, but the drop zone is multi-file (shows one chip per uploaded file, with in-UI reordering): 'Drop your CSV files' feeding a 'Run' button feeding a 'Merged preview' Textbox. Uploaded two small CSVs (north.csv, south.csv); preprocess returned the 2-item list ['/tmp/tmpw17n7aod.csv', '/tmp/tmp0wphuxz4.csv']; function returned: "3 data rows, columns seen: ['region', 'sales']". Gotcha verified the hard way: a bare list crashes preprocess with AttributeError: 'list' object has no attribute 'path' -- the internal payload must be the ListFiles dataclass. Tested with gradio 6.18.0.
gr.Files is just a template preset: gr.Files == gr.File(file_count='multiple'). In 6.x, file_count also accepts 'directory'.
import gradio as gr
def sniff(data: bytes) -> str:
return "Valid GIF header found." if data[:6] == b"GIF89a" else "Not a GIF."
demo = gr.Interface(
fn=sniff,
inputs=gr.File(type="binary", label="Tiny file, any type"),
outputs=gr.Textbox(label="Header check"),
)
demo.launch()* Running on local URL: http://127.0.0.1:7860
Renders an Interface page: single-file drop zone labeled 'Tiny file, any type' feeding a 'Run' button feeding a 'Header check' Textbox.
For a fake 9-byte fake GIF (GIF89a+0xff+three zero bytes) preprocess returned the raw bytes b'GIF89a\xff\x00\x00\x00\x00' (type bytes); function returned: 'Valid GIF header found.'
Error path, verified: uploading a .gif against gr.File(file_types=['.txt']) raises gr.Error("Invalid file type. Please upload a file that is one of these formats: ['.txt']") before the fn runs.
Tested with gradio 6.18.0.
type='binary' trades the temp path for bytes -- handy for hashing, virus scanning, or zipping straight through without a disk round-trip.
| Flag | Meaning |
|---|---|
file_types=[".pdf", ".docx"] | allowed extensions; frontend filter plus backend gr.Error -- with no extension, every file type is accepted. |
file_count='single' | 'multiple' | 'directory' | your fn gets a path str, a list of paths, or a folder; 'multiple' and 'directory' added in v6. |
type='filepath' | 'binary' | str temp path vs bytes; the str from postprocess is a NamedString that mostly behaves like str. |
gr.Files | template preset for gr.File(file_count='multiple') -- shorter to type, same component. |
allow_reordering=True | user can drag multi-file chips into order; input list order follows the UI. |
height=200 | drop-zone height in px (or str) -- set it when the zone swallows half the page. |
.upload() / .delete() / .download() | the file-specific listeners, alongside change / select / clear. |
gr.File has been the generic upload component since the earliest Gradio releases. file_types filtering arrived in 3.16.0 (2023-01-05, PR #2901 -- the same release that shipped Dropdown's multiselect); backend enforcement of file_types landed in 5.0.0-beta.6 (PR #9431).
In 4.x the frontend filter was advisory; PR #9431 moved the check server-side, so a malicious client bypassing the UI gets a gr.Error from the API route itself. That server-first stance is also why the error message text is generated by the backend.
The frontend uploads to /upload, then passes gradio.data_classes.FileData(path, orig_name, size, mime_type...) to gr.File.preprocess. For type='filepath' it hands your fn the temp path (a NamedString, a str subclass carrying the original filename); for type='binary' it slurps the file into bytes. Postprocess runs the reverse map, so gr.File also works as an output component -- hand it str paths and the UI gets download chips. The browser never touches your disk beyond the temp copy.