func FSHandler(root string, stripSlashes int) RequestHandler {
	fs := &FS{
		Root:               root,
		IndexNames:         []string{"index.html"},
		GenerateIndexPages: true,
		AcceptByteRange:    true,
	}
	if stripSlashes > 0 {
		fs.PathRewrite = NewPathSlashesStripper(stripSlashes)
	}
	return fs.NewRequestHandler()
}
FSHandler returns request handler serving static files from
the given root folder.

stripSlashes indicates how many leading slashes must be stripped
from requested path before searching requested file in the root folder.
Examples:

* stripSlashes = 0, original path: "/foo/bar", result: "/foo/bar"
* stripSlashes = 1, original path: "/foo/bar", result: "/bar"
* stripSlashes = 2, original path: "/foo/bar", result: ""

The returned request handler automatically generates index pages
for directories without index.html.

The returned handler caches requested file handles
for FSHandlerCacheDuration.
Make sure your program has enough 'max open files' limit aka
'ulimit -n' if root folder contains many files.

Do not create multiple request handler instances for the same
(root, stripSlashes) arguments - just reuse a single instance.
Otherwise goroutine leak will occur.
package fasthttp
import (
	"bytes"
	"errors"
	"fmt"
	"html"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
	"github.com/klauspost/compress/gzip"
)
func ServeFileBytesUncompressed(ctx *RequestCtx, path []byte) {
	ServeFileUncompressed(ctx, b2s(path))
}
func ServeFileUncompressed(ctx *RequestCtx, path string) {
	ctx.Request.Header.DelBytes(strAcceptEncoding)
	ServeFile(ctx, path)
}
func ServeFileBytes(ctx *RequestCtx, path []byte) {
	ServeFile(ctx, b2s(path))
}
func ServeFile(ctx *RequestCtx, path string) {
	rootFSOnce.Do(func() {
		rootFSHandler = rootFS.NewRequestHandler()
	})
	if len(path) == 0 || path[0] != '/' {
		var err error
		if path, err = filepath.Abs(path); err != nil {
			ctx.Logger().Printf("cannot resolve path %q to absolute file path: %s", path, err)
			ctx.Error("Internal Server Error", StatusInternalServerError)
			return
		}
	}
	ctx.Request.SetRequestURI(path)
	rootFSHandler(ctx)
}
var (
	rootFSOnce sync.Once
	rootFS     = &FS{
		Root:               "/",
		GenerateIndexPages: true,
		Compress:           true,
		AcceptByteRange:    true,
	}
	rootFSHandler RequestHandler
)
type PathRewriteFunc func(ctx *RequestCtx) []byte
func NewVHostPathRewriter(slashesCount int) PathRewriteFunc {
	return func(ctx *RequestCtx) []byte {
		path := stripLeadingSlashes(ctx.Path(), slashesCount)
		host := ctx.Host()
		if n := bytes.IndexByte(host, '/'); n >= 0 {
			host = nil
		}
		if len(host) == 0 {
			host = strInvalidHost
		}
		b := AcquireByteBuffer()
		b.B = append(b.B, '/')
		b.B = append(b.B, host...)
		b.B = append(b.B, path...)
		ctx.URI().SetPathBytes(b.B)
		ReleaseByteBuffer(b)
		return ctx.Path()
	}
}
var strInvalidHost = []byte("invalid-host")
func NewPathSlashesStripper(slashesCount int) PathRewriteFunc {
	return func(ctx *RequestCtx) []byte {
		return stripLeadingSlashes(ctx.Path(), slashesCount)
	}
}
func NewPathPrefixStripper(prefixSize int) PathRewriteFunc {
	return func(ctx *RequestCtx) []byte {
		path := ctx.Path()
		if len(path) >= prefixSize {
			path = path[prefixSize:]
		}
		return path
	}
}
type FS struct {
	noCopy noCopy
	Root string
	IndexNames []string
	GenerateIndexPages bool
	Compress bool
	AcceptByteRange bool
	PathRewrite PathRewriteFunc
	CacheDuration time.Duration
	once sync.Once
	h    RequestHandler
}
const FSCompressedFileSuffix = ".fasthttp.gz"
const FSHandlerCacheDuration = 10 * time.Second