fastimage is a tiny Go helper that sniffs image headers to extract dimensions without fully decoding the file, keeping it lightweight and suitable for hot paths like upload validation, image proxies, or metadata indexing pipelines. It also includes HTTP helpers for remote image probing via progressive range requests.
Typical use-cases:
- Validate user image uploads (type + size) without decoding the full image
- Implement lightweight image proxies / thumbnailers
- Crawl and index image metadata at scale
- Quickly check remote banner images / avatars in microservices
This repo started as a fork of rubenfonseca/fastimage and adds:
- AVIF support
- HTTP helpers for concurrent, range-based remote image probing
- Stream-aware
GetInfoReaderAPI for working withio.Reader
Big thanks and deep respect to @rubenfonseca, author of the original repo.
- Zero Dependencies - stdlib only
- High Performance - hand-written header parsing (no regex/wildcard)
- Wide format support – BMP, GIF, JPEG, MNG, PBM, PCX, PGM, PNG, PPM, PSD, RAS, TIFF, WebP, XBM, XPM, AVIF
- HTTP range helpers – progressive range fetching for remote images
- Reader API - stream-aware
GetInfoReaderfor files or network responses
Library (modules will fetch it automatically when you import):
import "github.com/kotylevskiy/fastimage"CLI:
go install github.com/kotylevskiy/fastimage/cmd/fastimage@latestpackage main
import (
"fmt"
"github.com/kotylevskiy/fastimage"
)
var data = []byte("RIFF,-\x00\x00WEBPVP8X\n\x00\x00\x00" +
"\x10\x00\x00\x00\x8f\x01\x00,\x01\x00VP8X\n\x00\x00\x00\x10\xb2" +
"\x01\x00\x00WEB\x01\x00VP8X\n\x00\x00\x00\x10\xb2\x01\x00" +
"\x00WEB\x01\x00VP8X\n\x00\x00\x00\x10\xb2\x01\x00\x00W" +
"EB\x01\x00VP8X\n\x00\x00\x00\x10\xb2\x01\x00\x00WEB"")
func main() {
fmt.Printf("%+v\n", fastimage.GetInfo(data))
}
// Output: {Type:webp Width:400 Height:301}resp, err := http.Get("https://example.com/image.jpg")
if err != nil {
// handle error
}
defer resp.Body.Close()
info, err := fastimage.GetInfoReader(resp.Body)
if err != nil {
// handle error
}
fmt.Printf("%+v\n", info)The HTTP helper is multithreaded and probes URLs concurrently (bounded by the concurrency options below).
urls := []string{
"https://example.com/a.jpg",
"https://example.com/b.png",
}
results := fastimage.GetHTTPImageInfo(context.Background(), urls)
for _, result := range results {
fmt.Printf("%s %+v err=%v\n", result.URL, result.Info, result.Error)
}GetHTTPImageInfo uses these defaults:
CONCURRENT_REQUESTS_FOR_REUSABLE_CONNECTIONS_DEFAULT = 20(per-origin when range is supported)CONCURRENT_REQUESTS_FOR_NON_REUSABLE_CONNECTIONS_DEFAULT = 5(per-origin when range is not supported)MAX_CONCURRENT_CONNECTIONS_GLOBAL_DEFAULT = 50(global cap across origins)
These limits cap concurrent requests; the HTTP transport also sets MaxConnsPerHost to ConcurrentRequestsReusable to avoid excessive connections per origin.
“Reusable” means the server supports HTTP range requests (206 Partial Content), so the client can reuse keep‑alive connections efficiently while fetching multiple byte ranges. “Not reusable” means range is unsupported; each probe may need a full response, so concurrency is reduced to avoid overloading the origin and wasting bandwidth.
For “browser‑like” behavior, the defaults are reasonable. The code uses 20 when range is supported by the remote host (requests multiplexed to a fewer number of connections) and falls back to 5 when it’s not.
To override them, use GetHTTPImageDataWithOptions:
options := fastimage.GetHTTPImageOptions{
ConcurrentRequestsReusable: 8,
ConcurrentRequestsNonReusable: 2,
MaxConcurrentConnections: 16,
}
results := fastimage.GetHTTPImageDataWithOptions(context.Background(), urls, options)$ go get github.com/kotylevskiy/fastimage/cmd/fastimage
$ fastimage banner.png
png image/png 320 50
$ fastimage https://example.com/banner.png
png image/png 320 50