golocaldownload
Turn a server directory into a browsable, searchable, downloadable file site
Stack
- Go
- Gin
- 内嵌静态资源
- Bootstrap 5
- Docker
Overview
golocaldownload turns a server directory into a browsable, searchable, downloadable file site. Written in Go + Gin, with page templates, static assets, and config templates all embedded via //go:embed — a release is a single executable. Unpack and run: no Node, no reverse proxy, no database required.
It's a convenient way to put build artifacts, asset bundles, or internal files behind one simple URL, for localhost or an intranet.
Features
- Directory browsing: breadcrumbs for level-by-level navigation; directories sort before files, with size and modification time shown
- Type-based icons: archives / images / videos / audio / documents / spreadsheets / code distinguished by extension, unknown ones fall back to a generic document icon
- Global search: case-insensitive substring match on file names, with one-click jump to the containing directory (max 500 results)
- File download: non-ASCII and spaced file names encoded per RFC 5987, so browsers never save garbled names
- One-click copy links: files copy as absolute download URLs (browsers,
wget, and download managers work directly); directories copy a locating page URL - Theme switching: light / follow system / dark, applied on first paint so there's no white flash in dark mode
- List / grid views: list for sizes and times, grid for recognizing content by icon; switching never re-requests the API
- Path traversal protection: listing, search, and download are confined to the download library —
../is rejected outright - Graceful shutdown:
Ctrl+CorSIGTERMdrains in-flight downloads instead of cutting off large transfers
Quick Start
Requires Go 1.23 or later.
# Run directly in dev mode
go run .
# Or build then run
go build -o golocaldownload . && ./golocaldownload
On startup it prints the version, config source, download library directory, and accessible URLs:
golocaldownload dev (debug)
Config source: config/env.ini
Download library: E:\download_lib
Local access: http://127.0.0.1:9801
LAN access: http://192.168.1.10:9801
Drop the files you want to serve into the download library directory and refresh the page. It listens on 9801 by default, and the library is download_lib/ under the working directory (created automatically if missing).
Deployment Options
| Scenario | Option |
|---|---|
| Modifying code / long-term maintenance | Build from source (go build) |
| Just run it | Download the archive for your platform from Releases and extract |
| Docker available | docker build locally, or docker compose up -d |
| Prebuilt image | Pull from Docker Hub or Alibaba Cloud ACR |
Pulling a prebuilt image is the least work:
docker pull tutudev99/golocaldownload:latest
docker run -p 9801:9801 --name golocaldownload \
-v /home/download_lib:/root/download_lib --restart always -d \
tutudev99/golocaldownload:latest
The right side of the mount must always be
/root/download_lib; fill in the left side per your host OS. On Windows use a drive-letter path (e.g.D:/download_lib) rather than/home/...— the latter is treated as a path inside Docker Desktop's WSL2 VM, so the data isn't on Windows, is invisible to Explorer, and gets wiped by Docker Desktop's "Reset to factory defaults".
Images cover both linux/amd64 and linux/arm64, so ARM servers and Apple Silicon run them directly.
Configuration
The config file is INI format. Sources, highest to lowest priority:
- The file specified by the
-configflag /GLD_CONFIGenv var env.iniin the working directoryconfig/env.iniin the working directory- The embedded
env.ini.<GLD_ENV>in the binary (releaseby default)
| Key | Default | Description |
|---|---|---|
env_mode |
release |
debug / release / test |
download_lib_path |
download_lib |
Download library directory; multi-level relative or absolute paths, created automatically |
display_lib_path |
empty | Path shown on the page; when empty, the container-mapped host directory is auto-detected |
server.http_port |
9801 |
Listening port |
Four templates (env.ini.debug / .local / .release / .test) are embedded in the binary, so a fresh clone builds with plain go build — no need to rename env.ini.local first.
API
| Method & Path | Parameters | Description |
|---|---|---|
GET / |
- | The page |
GET /api/list |
path (relative to the download library; empty for root) |
List directory contents |
POST /api/search |
form field keyword |
Search by file name, max 500 results |
GET /api/download |
data (base64url-encoded file path) |
Download a file |
On error it returns the status code with {"error": "..."}: path traversal 400 / 403, missing target 404, unreadable directory 403, otherwise 500.
Implementation Notes
- Every request path goes through traversal validation: all paths pass through
common.SafeJoin— NUL-containing paths are rejected first, separators normalized and leading slashes stripped, thenfilepath.Relconfirms the result stays inside the root; the download endpoint additionally verifies the absolute path falls within the root Recoveryalways enabled: handlers only return status codes and messages, so a single "directory not found" can never take down the process in any mode- Config access is default-valued and panic-free: an invalid
env_modefalls back torelease, andGetInt/GetBool/GetDurationreturn defaults when config is missing or malformed - Search uses
filepath.WalkDirwith a result cap: no symlink following, unreadable entries skipped, capped at 500 results so one search can't blow up the response body - Shows the host directory inside containers: reads
/proc/self/mountinfoand picks the record whose mount point is the longest prefix of the library path, using its source directory as the display path - Reproducible artifacts:
tools/packuses the Go standard library to write 0755 explicitly and pin timestamps, so the same source produces identical artifacts and checksums on every platform with either script
Known Limitations
- No authentication — anyone who can reach the port can browse and download everything; by default this suits localhost or an intranet only
- No HTTPS — real TLS must be terminated by a reverse proxy such as Nginx or Caddy
- Search is a full traversal — every request walks the library with
WalkDir; very large libraries will be slow - Only one download library root — no upload, delete, or rename operations
- No resumable downloads or rate limiting — large files at high concurrency can saturate bandwidth
- Docker images cover only linux/amd64 and linux/arm64 — for other architectures run the binary directly on the host
License
Last updated