156 lines
4.3 KiB
Go
156 lines
4.3 KiB
Go
package rpack
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"crypto/md5"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"mime"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type RPACK struct {
|
|
Compressed bool
|
|
Modified int64
|
|
Mime string
|
|
Content string
|
|
raw []byte
|
|
}
|
|
|
|
var guzpool = sync.Pool{
|
|
New: func() interface{} {
|
|
return &gzip.Reader{}
|
|
}}
|
|
|
|
func Pack(root, output, pkgname, funcname string, main bool) {
|
|
root = strings.TrimSuffix(root, "/")
|
|
if root == "" || output == "" {
|
|
return
|
|
}
|
|
if pkgname == "" || main {
|
|
pkgname = "main"
|
|
}
|
|
if funcname == "" {
|
|
funcname = "resources"
|
|
}
|
|
funcname = strings.ToUpper(funcname[:1]) + funcname[1:]
|
|
entries := map[string]*RPACK{}
|
|
compressor, _ := gzip.NewWriterLevel(nil, gzip.BestCompression)
|
|
count := 0
|
|
size := int64(0)
|
|
start := time.Now()
|
|
filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
rpath := strings.TrimPrefix(path, root+"/")
|
|
if info.Mode()&os.ModeType == 0 {
|
|
for _, part := range strings.Split(rpath, "/") {
|
|
if len(part) > 0 && part[0] == '.' {
|
|
return nil
|
|
}
|
|
}
|
|
pack := &RPACK{Modified: info.ModTime().Unix(), Mime: "text/plain"}
|
|
if mime := mime.TypeByExtension(filepath.Ext(rpath)); mime != "" {
|
|
pack.Mime = mime
|
|
}
|
|
content, _ := ioutil.ReadFile(path)
|
|
compressed := bytes.Buffer{}
|
|
compressor.Reset(&compressed)
|
|
compressor.Write(content)
|
|
compressor.Close()
|
|
if compressed.Len() < len(content) {
|
|
pack.Content = base64.StdEncoding.EncodeToString(compressed.Bytes())
|
|
pack.Compressed = true
|
|
} else {
|
|
pack.Content = base64.StdEncoding.EncodeToString(content)
|
|
}
|
|
entries[rpath] = pack
|
|
fmt.Fprintf(os.Stderr, "\r%-120.120s ", rpath)
|
|
count++
|
|
size += info.Size()
|
|
}
|
|
return nil
|
|
})
|
|
fmt.Fprintf(os.Stderr, "\r%-120.120s\rpacked %d file(s) %d byte(s) in %v\n", "", count, size, time.Now().Sub(start))
|
|
if handle, err := os.OpenFile(output, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644); err == nil {
|
|
random := make([]byte, 64)
|
|
rand.Seed(time.Now().UnixNano() + int64(os.Getpid()))
|
|
rand.Read(random)
|
|
uid := fmt.Sprintf("rpack%8.8x", md5.Sum(random))
|
|
fmt.Fprintf(handle,
|
|
`package %s
|
|
|
|
import (
|
|
"net/http"
|
|
"github.com/pyke369/golang-support/rpack"
|
|
)
|
|
|
|
var %s map[string]*rpack.RPACK = map[string]*rpack.RPACK {
|
|
`,
|
|
pkgname, uid)
|
|
for path, entry := range entries {
|
|
fmt.Fprintf(handle,
|
|
` "%s": &rpack.RPACK{Compressed:%t, Modified:%d, Mime:"%s", Content:"%s"},
|
|
`, path, entry.Compressed, entry.Modified, entry.Mime, entry.Content)
|
|
}
|
|
fmt.Fprintf(handle,
|
|
`}
|
|
|
|
func %s() http.Handler {
|
|
return rpack.Serve(%s)
|
|
}
|
|
`, funcname, uid)
|
|
if main {
|
|
fmt.Fprintf(handle,
|
|
`
|
|
func main() {
|
|
http.Handle("/resources/", http.StripPrefix("/resources/", %s()))
|
|
http.ListenAndServe(":8000", nil)
|
|
}
|
|
`, funcname)
|
|
}
|
|
handle.Close()
|
|
}
|
|
}
|
|
|
|
func Serve(pack map[string]*RPACK) http.Handler {
|
|
return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
|
var err error
|
|
if pack == nil || pack[request.URL.Path] == nil {
|
|
response.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
if pack[request.URL.Path].raw == nil {
|
|
if pack[request.URL.Path].raw, err = base64.StdEncoding.DecodeString(pack[request.URL.Path].Content); err != nil {
|
|
response.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
}
|
|
resource := pack[request.URL.Path]
|
|
response.Header().Set("Content-Type", resource.Mime)
|
|
if strings.Index(request.Header.Get("Accept-Encoding"), "gzip") >= 0 && request.Header.Get("Range") == "" && resource.Compressed {
|
|
response.Header().Set("Content-Encoding", "gzip")
|
|
response.Header().Set("Content-Length", fmt.Sprintf("%d", len(resource.raw)))
|
|
http.ServeContent(response, request, request.URL.Path, time.Unix(resource.Modified, 0), bytes.NewReader(resource.raw))
|
|
} else {
|
|
if resource.Compressed {
|
|
decompressor := guzpool.Get().(*gzip.Reader)
|
|
decompressor.Reset(bytes.NewReader(resource.raw))
|
|
if raw, err := ioutil.ReadAll(decompressor); err == nil {
|
|
http.ServeContent(response, request, request.URL.Path, time.Unix(resource.Modified, 0), bytes.NewReader(raw))
|
|
}
|
|
decompressor.Close()
|
|
guzpool.Put(decompressor)
|
|
} else {
|
|
http.ServeContent(response, request, request.URL.Path, time.Unix(resource.Modified, 0), bytes.NewReader(resource.raw))
|
|
}
|
|
}
|
|
})
|
|
}
|