← Back to all tools

golocaldownload

Turn a server directory into a browsable, searchable, downloadable file site

  • Language: Go 1.23+
  • License: MIT
  • Platforms: 15 platforms (Windows / Linux / macOS / FreeBSD)

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+C or SIGTERM drains 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:

  1. The file specified by the -config flag / GLD_CONFIG env var
  2. env.ini in the working directory
  3. config/env.ini in the working directory
  4. The embedded env.ini.<GLD_ENV> in the binary (release by 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, then filepath.Rel confirms the result stays inside the root; the download endpoint additionally verifies the absolute path falls within the root
  • Recovery always 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_mode falls back to release, and GetInt / GetBool / GetDuration return defaults when config is missing or malformed
  • Search uses filepath.WalkDir with 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/mountinfo and 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/pack uses 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

  1. No authentication — anyone who can reach the port can browse and download everything; by default this suits localhost or an intranet only
  2. No HTTPS — real TLS must be terminated by a reverse proxy such as Nginx or Caddy
  3. Search is a full traversal — every request walks the library with WalkDir; very large libraries will be slow
  4. Only one download library root — no upload, delete, or rename operations
  5. No resumable downloads or rate limiting — large files at high concurrency can saturate bandwidth
  6. Docker images cover only linux/amd64 and linux/arm64 — for other architectures run the binary directly on the host

License

MIT · Source: GitHub · Releases: Releases

Last updated