github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/emb/embed.go (about)

     1  package emb
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"crypto/md5"
     7  	"encoding/hex"
     8  	"io"
     9  	"io/fs"
    10  	"mime"
    11  	"net/http"
    12  	"path/filepath"
    13  	"strings"
    14  )
    15  
    16  // from https://github.com/pyros2097/go-embed
    17  
    18  func AssetString(f fs.FS, name string, useGzip bool) string {
    19  	return string(AssetBytes(f, name, useGzip))
    20  }
    21  
    22  func AssetBytes(f fs.FS, name string, useGzip bool) (data []byte) {
    23  	data, _, _, _ = Asset(f, name, useGzip)
    24  	return data
    25  }
    26  
    27  // Asset Gets the file from embed.FS if debug otherwise gets it from the stored
    28  // data returns the data, the md5 hash of its content and its content type
    29  // and error if it is not found.
    30  func Asset(f fs.FS, name string, useGzip bool) (data []byte, hash, contentType string, err error) {
    31  	if strings.HasPrefix(name, "/") {
    32  		name = name[1:]
    33  	}
    34  
    35  	var fn fs.File
    36  	fn, err = f.Open(name)
    37  	if err != nil {
    38  		return nil, "", "", err
    39  	}
    40  	data, err = io.ReadAll(fn)
    41  	if err != nil {
    42  		return nil, "", "", err
    43  	}
    44  
    45  	if useGzip && len(data) > 0 {
    46  		var b bytes.Buffer
    47  		w := gzip.NewWriter(&b)
    48  		_, _ = w.Write(data)
    49  		_ = w.Close()
    50  		data = b.Bytes()
    51  	}
    52  
    53  	sum := md5.Sum(data)
    54  	contentType = mime.TypeByExtension(filepath.Ext(name))
    55  	return data, hex.EncodeToString(sum[:]), contentType, nil
    56  }
    57  
    58  func FileHandler(f fs.FS, name string) http.HandlerFunc {
    59  	return func(w http.ResponseWriter, r *http.Request) { ServeFile(f, name, w, r) }
    60  }
    61  
    62  func ServeFile(f fs.FS, name string, w http.ResponseWriter, r *http.Request) {
    63  	ServeFileGzip(f, name, w, r, true)
    64  }
    65  
    66  func ServeFileGzip(f fs.FS, name string, w http.ResponseWriter, r *http.Request, useGzip bool) {
    67  	if useGzip && w.Header().Get("Content-Encoding") == "gzip" {
    68  		useGzip = false // if already gzip outside
    69  	}
    70  
    71  	data, hash, contentType, err := Asset(f, name, useGzip)
    72  	if err != nil {
    73  		http.Error(w, err.Error(), http.StatusInternalServerError)
    74  		return
    75  	}
    76  
    77  	if r.Header.Get("If-None-Match") == hash {
    78  		w.WriteHeader(http.StatusNotModified)
    79  		return
    80  	}
    81  
    82  	if useGzip {
    83  		w.Header().Set("Content-Encoding", "gzip")
    84  	}
    85  	w.Header().Set("Content-Type", contentType)
    86  	w.Header().Add("Cache-Control", "public, max-age=31536000")
    87  	w.Header().Add("ETag", hash)
    88  	w.WriteHeader(http.StatusOK)
    89  	_, _ = w.Write(data)
    90  }