uploadserver plus an in-browser previewer for developer files (Markdown, JSON, code, CSV, diffs, images). No build step; works offline on a LAN.
Project description
uploadserver-preview
An in-browser previewer bolted onto uploadserver.
Browse a directory over HTTP and click a file to see it rendered instead of
downloaded: Markdown as formatted prose, JSON as a collapsible tree, source code
with syntax highlighting and line numbers, CSV/TSV as tables, unified diffs the
way GitHub shows them, and images inline.
Everything renders client-side with vendored JavaScript — no build step, no
CDN, no outbound network. It works on an air-gapped LAN, which is where a file
server like this usually lives. Uploads and every other uploadserver feature
(basic auth, TLS/mTLS, --directory, --theme, ...) are untouched.
Install
Published on PyPI as uploadserver-preview.
With uv (recommended — no separate install step):
uvx uploadserver-preview # run once, in an ephemeral environment
uvx uploadserver-preview 9000 -d /srv
Or install it properly, with either uv or pip:
uv tool install uploadserver-preview
pip install uploadserver-preview
This pulls in uploadserver as a dependency. Python 3.9+.
Use
Exactly like uploadserver — same arguments, same behaviour, plus previews:
uploadserver-preview # serve ./ on :8000
uploadserver-preview 9000 -d /srv # port 9000, serve /srv
uploadserver-preview --theme dark
uploadserver-preview --basic-auth me:secret
python -m uploadserver_preview 8080 # module form also works
Then open http://<host>:8000/, browse, and click a file. Each row shows a small
type tag (md, json, py, csv, …); previewable files open in the viewer,
anything else downloads. Every file also has a raw link.
Flags
All of uploadserver's flags are accepted (port, --directory/-d,
--bind/-b, --theme, --allow-replace, --server-certificate/-c,
--client-certificate, --basic-auth, --basic-auth-upload, --cgi), plus:
| Flag | Effect |
|---|---|
--no-preview |
Disable the previewer; behave like plain uploadserver. |
--cgi also disables the previewer (CGI mode is left to stock uploadserver).
What it renders
| Type | Extensions | Rendered as |
|---|---|---|
| Markdown | .md .markdown .mdown .mkd |
Sanitized HTML prose; fenced code highlighted |
| JSON | .json .geojson .ipynb |
Collapsible tree (falls back to highlighted source if invalid) |
| Config | .yaml .yml .toml .ini .cfg .conf |
Highlighted source (comments preserved) |
| Code | .py .js .ts .tsx .rs .go .java .c .cpp .rb .php .lua .sql .sh and more |
Highlighted, line-numbered |
| Tables | .csv .tsv |
Sortable-width HTML table with row numbers |
| Diffs | .diff .patch |
Line-by-line diff view |
| Images | .png .jpg .jpeg .gif .webp .bmp .ico .avif .svg |
Inline, with dimensions |
| Markup | .html .htm .xml .xhtml |
Source only — never executed |
Unrecognized extensions are shown as text when they look textual, and flagged as binary otherwise. Text rendering is capped at 3 MB and tables at 5,000 rows to keep the browser responsive; the raw link always serves the full file.
How it works
uploadserver exposes its request handler as a module-level name that
serve_forever() resolves at call time. This package subclasses that handler and
swaps it in before serving, so uploads and auth keep working while a few extra
routes are added:
- A browser navigating to a previewable file's own URL (detected via
Sec-Fetch-Dest: document) gets the explorer shell instead of raw bytes; the client then fetches the file same-origin, dispatches on extension, and renders it.curl/wget,fetch(), iframes — and anything with?raw— keep getting the raw file, so scripted downloads behave like stockuploadserver. GET /__view__?path=…returns a small static viewer shell (kept for old links). The server never reflectspathinto HTML.GET /__preview_asset__/<name>serves the vendored JS/CSS by basename from a fixed whitelist (traversal-proof).- Directory listings are replaced with a richer, themed listing that adds view and raw links and the type tags.
Everything else — raw files, /upload, redirects — falls through to
uploadserver unchanged.
Security notes
- Uploaded content is untrusted. Markdown is sanitized with DOMPurify before
it touches the DOM. Code, JSON, YAML, CSV, and diffs are escaped or inserted as
text. HTML/SVG/XML files are shown as source and never executed; images are
loaded via
<img>, where scripts do not run. - A strict Content-Security-Policy is sent on preview pages:
script-src 'self'(all JS is external — there are no inline scripts),default-src 'self',object-src 'none'. Inline styles are permitted because the JSON component and highlight/diff themes rely on them. - Preview routes and assets are behind the same basic auth as the rest of the server, so nothing is exposed before authentication.
- This is still a simple file server. Don't expose it to the public internet
without auth + TLS, exactly as with stock
uploadserver.
Offline / vendored assets
All rendering libraries live under uploadserver_preview/assets/ and are served
locally, so the previewer needs no internet access. Bundled: highlight.js
(common languages + Dockerfile), marked, DOMPurify, PapaParse, diff2html (core),
and @andypf/json-viewer. See LICENSE for their licenses.
Limitations
- No preview for PDF or Office documents (they open via the raw link, and modern browsers render PDFs natively).
- Diffs are colored by line; intra-line syntax highlighting inside diffs is omitted to keep the bundle small.
- CGI mode (
--cgi) is served by stockuploadserverwithout previews.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uploadserver_preview-0.1.13.tar.gz.
File metadata
- Download URL: uploadserver_preview-0.1.13.tar.gz
- Upload date:
- Size: 160.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
110d4391fda89209dadfcbd9a8a2294181f945350c1c0143b96c41f9c81505f1
|
|
| MD5 |
614ab9a29a848d90f5df7641396b8943
|
|
| BLAKE2b-256 |
77742c81cfbafe90bdcde2481cf83e1c3c535b1e6ab94e4c6f311db605debe21
|
File details
Details for the file uploadserver_preview-0.1.13-py3-none-any.whl.
File metadata
- Download URL: uploadserver_preview-0.1.13-py3-none-any.whl
- Upload date:
- Size: 164.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
495e0e7237b7dd96dd95424bd5ddc97e4714a0cc5f3022f7afd4eea8dcb07cf0
|
|
| MD5 |
f92c9f3aa91003e719320e6fcc2a12c8
|
|
| BLAKE2b-256 |
193c69b537dc144c7e240c0f19a9ea04b42865946a21b3e6a2516dc113da4257
|