gf/os/gres/gres_resource.go

238 lines
6.3 KiB
Go
Raw Normal View History

2021-01-07 19:34:46 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2019-08-13 13:45:01 +08:00
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gres
import (
"context"
2019-08-13 13:45:01 +08:00
"fmt"
"github.com/gogf/gf/internal/intlog"
2019-09-01 21:34:15 +08:00
"os"
2019-08-13 13:45:01 +08:00
"path/filepath"
"strings"
"github.com/gogf/gf/os/gtime"
2019-08-13 13:45:01 +08:00
"github.com/gogf/gf/container/gtree"
"github.com/gogf/gf/os/gfile"
)
type Resource struct {
tree *gtree.BTree
}
const (
gDEFAULT_TREE_M = 100
)
2019-08-13 21:06:11 +08:00
// New creates and returns a new resource object.
2019-08-13 13:45:01 +08:00
func New() *Resource {
return &Resource{
tree: gtree.NewBTree(gDEFAULT_TREE_M, func(v1, v2 interface{}) int {
return strings.Compare(v1.(string), v2.(string))
}),
}
}
2019-08-13 21:06:11 +08:00
// Add unpacks and adds the <content> into current resource object.
// The unnecessary parameter <prefix> indicates the prefix
// for each file storing into current resource object.
func (r *Resource) Add(content string, prefix ...string) error {
2019-08-13 13:45:01 +08:00
files, err := UnpackContent(content)
if err != nil {
intlog.Printf(context.TODO(), "Add resource files failed: %v", err)
2019-08-13 13:45:01 +08:00
return err
}
namePrefix := ""
if len(prefix) > 0 {
namePrefix = prefix[0]
}
for i := 0; i < len(files); i++ {
2019-08-14 22:03:52 +08:00
files[i].resource = r
r.tree.Set(namePrefix+files[i].file.Name, files[i])
2019-08-13 13:45:01 +08:00
}
intlog.Printf(context.TODO(), "Add %d files to resource manager", r.tree.Size())
2019-08-13 13:45:01 +08:00
return nil
}
2019-08-13 21:06:11 +08:00
// Load loads, unpacks and adds the data from <path> into current resource object.
// The unnecessary parameter <prefix> indicates the prefix
// for each file storing into current resource object.
2019-08-13 13:45:01 +08:00
func (r *Resource) Load(path string, prefix ...string) error {
realPath, err := gfile.Search(path)
if err != nil {
return err
}
return r.Add(gfile.GetContents(realPath), prefix...)
2019-08-13 13:45:01 +08:00
}
2019-08-13 21:06:11 +08:00
// Get returns the file with given path.
2019-08-13 13:45:01 +08:00
func (r *Resource) Get(path string) *File {
2019-08-19 22:54:37 +08:00
if path == "" {
return nil
}
path = strings.Replace(path, "\\", "/", -1)
2021-01-07 19:34:46 +08:00
path = strings.Replace(path, "//", "/", -1)
2019-08-14 22:03:52 +08:00
if path != "/" {
for path[len(path)-1] == '/' {
path = path[:len(path)-1]
}
}
2019-08-13 13:45:01 +08:00
result := r.tree.Get(path)
if result != nil {
return result.(*File)
}
return nil
}
2019-08-14 22:03:52 +08:00
// GetWithIndex searches file with <path>, if the file is directory
// it then does index files searching under this directory.
//
// GetWithIndex is usually used for http static file service.
func (r *Resource) GetWithIndex(path string, indexFiles []string) *File {
2020-05-14 20:32:01 +08:00
// Necessary for double char '/' replacement in prefix.
path = strings.Replace(path, "\\", "/", -1)
2021-01-07 19:34:46 +08:00
path = strings.Replace(path, "//", "/", -1)
2019-08-14 22:03:52 +08:00
if path != "/" {
for path[len(path)-1] == '/' {
path = path[:len(path)-1]
}
}
if file := r.Get(path); file != nil {
if len(indexFiles) > 0 && file.FileInfo().IsDir() {
var f *File
for _, name := range indexFiles {
if f = r.Get(path + "/" + name); f != nil {
return f
}
}
}
return file
}
return nil
}
2019-08-16 00:29:14 +08:00
// GetContent directly returns the content of <path>.
func (r *Resource) GetContent(path string) []byte {
file := r.Get(path)
if file != nil {
return file.Content()
}
return nil
}
// Contains checks whether the <path> exists in current resource object.
func (r *Resource) Contains(path string) bool {
return r.Get(path) != nil
}
2019-08-19 21:02:44 +08:00
// IsEmpty checks and returns whether the resource manager is empty.
func (r *Resource) IsEmpty() bool {
return r.tree.IsEmpty()
}
2019-08-31 18:04:12 +08:00
// ScanDir returns the files under the given path, the parameter <path> should be a folder type.
2019-08-13 21:06:11 +08:00
//
// The pattern parameter <pattern> supports multiple file name patterns,
// using the ',' symbol to separate multiple patterns.
//
// It scans directory recursively if given parameter <recursive> is true.
//
// Note that the returned files does not contain given parameter <path>.
2019-08-31 18:04:12 +08:00
func (r *Resource) ScanDir(path string, pattern string, recursive ...bool) []*File {
isRecursive := false
if len(recursive) > 0 {
isRecursive = recursive[0]
}
return r.doScanDir(path, pattern, isRecursive, false)
}
// ScanDirFile returns all sub-files with absolute paths of given <path>,
// It scans directory recursively if given parameter <recursive> is true.
//
// Note that it returns only files, exclusive of directories.
func (r *Resource) ScanDirFile(path string, pattern string, recursive ...bool) []*File {
isRecursive := false
if len(recursive) > 0 {
isRecursive = recursive[0]
}
return r.doScanDir(path, pattern, isRecursive, true)
}
// doScanDir is an internal method which scans directory
// and returns the absolute path list of files that are not sorted.
//
// The pattern parameter <pattern> supports multiple file name patterns,
// using the ',' symbol to separate multiple patterns.
//
// It scans directory recursively if given parameter <recursive> is true.
func (r *Resource) doScanDir(path string, pattern string, recursive bool, onlyFile bool) []*File {
path = strings.Replace(path, "\\", "/", -1)
2021-01-07 19:34:46 +08:00
path = strings.Replace(path, "//", "/", -1)
2019-08-13 13:45:01 +08:00
if path != "/" {
2019-08-14 22:03:52 +08:00
for path[len(path)-1] == '/' {
path = path[:len(path)-1]
}
2019-08-13 13:45:01 +08:00
}
2020-05-14 20:32:01 +08:00
var (
name = ""
files = make([]*File, 0)
length = len(path)
patterns = strings.Split(pattern, ",")
)
2019-08-13 13:45:01 +08:00
for i := 0; i < len(patterns); i++ {
patterns[i] = strings.TrimSpace(patterns[i])
}
2019-08-14 22:03:52 +08:00
// Used for type checking for first entry.
first := true
r.tree.IteratorFrom(path, true, func(key, value interface{}) bool {
if first {
if !value.(*File).FileInfo().IsDir() {
return false
}
first = false
}
2019-08-31 18:04:12 +08:00
if onlyFile && value.(*File).FileInfo().IsDir() {
return true
}
2019-08-13 13:45:01 +08:00
name = key.(string)
2019-08-14 22:03:52 +08:00
if len(name) <= length {
return true
}
2019-08-13 13:45:01 +08:00
if path != name[:length] {
return false
}
2019-08-31 18:04:12 +08:00
// To avoid of, eg: /i18n and /i18n-dir
if !first && name[length] != '/' {
return true
}
if !recursive {
2019-08-14 22:03:52 +08:00
if strings.IndexByte(name[length+1:], '/') != -1 {
2019-08-13 21:06:11 +08:00
return true
2019-08-13 13:45:01 +08:00
}
}
for _, p := range patterns {
if match, err := filepath.Match(p, gfile.Basename(name)); err == nil && match {
files = append(files, value.(*File))
return true
}
}
return true
})
return files
}
2019-08-13 21:06:11 +08:00
// Dump prints the files of current resource object.
2019-08-13 13:45:01 +08:00
func (r *Resource) Dump() {
2019-09-01 21:34:15 +08:00
var info os.FileInfo
2019-08-13 13:45:01 +08:00
r.tree.Iterator(func(key, value interface{}) bool {
2019-09-01 21:34:15 +08:00
info = value.(*File).FileInfo()
fmt.Printf("%v %7s %s\n", gtime.New(info.ModTime()).ISO8601(), gfile.FormatSize(info.Size()), key)
2019-08-13 13:45:01 +08:00
return true
})
2019-08-13 21:06:11 +08:00
fmt.Printf("TOTAL FILES: %d\n", r.tree.Size())
2019-08-13 13:45:01 +08:00
}