github.com/gogf/gf@v1.16.9/os/gres/gres_resource.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gres
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"github.com/gogf/gf/internal/intlog"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  
    17  	"github.com/gogf/gf/os/gtime"
    18  
    19  	"github.com/gogf/gf/container/gtree"
    20  	"github.com/gogf/gf/os/gfile"
    21  )
    22  
    23  type Resource struct {
    24  	tree *gtree.BTree
    25  }
    26  
    27  const (
    28  	gDEFAULT_TREE_M = 100
    29  )
    30  
    31  // New creates and returns a new resource object.
    32  func New() *Resource {
    33  	return &Resource{
    34  		tree: gtree.NewBTree(gDEFAULT_TREE_M, func(v1, v2 interface{}) int {
    35  			return strings.Compare(v1.(string), v2.(string))
    36  		}),
    37  	}
    38  }
    39  
    40  // Add unpacks and adds the <content> into current resource object.
    41  // The unnecessary parameter <prefix> indicates the prefix
    42  // for each file storing into current resource object.
    43  func (r *Resource) Add(content string, prefix ...string) error {
    44  	files, err := UnpackContent(content)
    45  	if err != nil {
    46  		intlog.Printf(context.TODO(), "Add resource files failed: %v", err)
    47  		return err
    48  	}
    49  	namePrefix := ""
    50  	if len(prefix) > 0 {
    51  		namePrefix = prefix[0]
    52  	}
    53  	for i := 0; i < len(files); i++ {
    54  		files[i].resource = r
    55  		r.tree.Set(namePrefix+files[i].file.Name, files[i])
    56  	}
    57  	intlog.Printf(context.TODO(), "Add %d files to resource manager", r.tree.Size())
    58  	return nil
    59  }
    60  
    61  // Load loads, unpacks and adds the data from <path> into current resource object.
    62  // The unnecessary parameter <prefix> indicates the prefix
    63  // for each file storing into current resource object.
    64  func (r *Resource) Load(path string, prefix ...string) error {
    65  	realPath, err := gfile.Search(path)
    66  	if err != nil {
    67  		return err
    68  	}
    69  	return r.Add(gfile.GetContents(realPath), prefix...)
    70  }
    71  
    72  // Get returns the file with given path.
    73  func (r *Resource) Get(path string) *File {
    74  	if path == "" {
    75  		return nil
    76  	}
    77  	path = strings.Replace(path, "\\", "/", -1)
    78  	path = strings.Replace(path, "//", "/", -1)
    79  	if path != "/" {
    80  		for path[len(path)-1] == '/' {
    81  			path = path[:len(path)-1]
    82  		}
    83  	}
    84  	result := r.tree.Get(path)
    85  	if result != nil {
    86  		return result.(*File)
    87  	}
    88  	return nil
    89  }
    90  
    91  // GetWithIndex searches file with <path>, if the file is directory
    92  // it then does index files searching under this directory.
    93  //
    94  // GetWithIndex is usually used for http static file service.
    95  func (r *Resource) GetWithIndex(path string, indexFiles []string) *File {
    96  	// Necessary for double char '/' replacement in prefix.
    97  	path = strings.Replace(path, "\\", "/", -1)
    98  	path = strings.Replace(path, "//", "/", -1)
    99  	if path != "/" {
   100  		for path[len(path)-1] == '/' {
   101  			path = path[:len(path)-1]
   102  		}
   103  	}
   104  	if file := r.Get(path); file != nil {
   105  		if len(indexFiles) > 0 && file.FileInfo().IsDir() {
   106  			var f *File
   107  			for _, name := range indexFiles {
   108  				if f = r.Get(path + "/" + name); f != nil {
   109  					return f
   110  				}
   111  			}
   112  		}
   113  		return file
   114  	}
   115  	return nil
   116  }
   117  
   118  // GetContent directly returns the content of <path>.
   119  func (r *Resource) GetContent(path string) []byte {
   120  	file := r.Get(path)
   121  	if file != nil {
   122  		return file.Content()
   123  	}
   124  	return nil
   125  }
   126  
   127  // Contains checks whether the <path> exists in current resource object.
   128  func (r *Resource) Contains(path string) bool {
   129  	return r.Get(path) != nil
   130  }
   131  
   132  // IsEmpty checks and returns whether the resource manager is empty.
   133  func (r *Resource) IsEmpty() bool {
   134  	return r.tree.IsEmpty()
   135  }
   136  
   137  // ScanDir returns the files under the given path, the parameter <path> should be a folder type.
   138  //
   139  // The pattern parameter <pattern> supports multiple file name patterns,
   140  // using the ',' symbol to separate multiple patterns.
   141  //
   142  // It scans directory recursively if given parameter <recursive> is true.
   143  //
   144  // Note that the returned files does not contain given parameter <path>.
   145  func (r *Resource) ScanDir(path string, pattern string, recursive ...bool) []*File {
   146  	isRecursive := false
   147  	if len(recursive) > 0 {
   148  		isRecursive = recursive[0]
   149  	}
   150  	return r.doScanDir(path, pattern, isRecursive, false)
   151  }
   152  
   153  // ScanDirFile returns all sub-files with absolute paths of given <path>,
   154  // It scans directory recursively if given parameter <recursive> is true.
   155  //
   156  // Note that it returns only files, exclusive of directories.
   157  func (r *Resource) ScanDirFile(path string, pattern string, recursive ...bool) []*File {
   158  	isRecursive := false
   159  	if len(recursive) > 0 {
   160  		isRecursive = recursive[0]
   161  	}
   162  	return r.doScanDir(path, pattern, isRecursive, true)
   163  }
   164  
   165  // doScanDir is an internal method which scans directory
   166  // and returns the absolute path list of files that are not sorted.
   167  //
   168  // The pattern parameter <pattern> supports multiple file name patterns,
   169  // using the ',' symbol to separate multiple patterns.
   170  //
   171  // It scans directory recursively if given parameter <recursive> is true.
   172  func (r *Resource) doScanDir(path string, pattern string, recursive bool, onlyFile bool) []*File {
   173  	path = strings.Replace(path, "\\", "/", -1)
   174  	path = strings.Replace(path, "//", "/", -1)
   175  	if path != "/" {
   176  		for path[len(path)-1] == '/' {
   177  			path = path[:len(path)-1]
   178  		}
   179  	}
   180  	var (
   181  		name     = ""
   182  		files    = make([]*File, 0)
   183  		length   = len(path)
   184  		patterns = strings.Split(pattern, ",")
   185  	)
   186  	for i := 0; i < len(patterns); i++ {
   187  		patterns[i] = strings.TrimSpace(patterns[i])
   188  	}
   189  	// Used for type checking for first entry.
   190  	first := true
   191  	r.tree.IteratorFrom(path, true, func(key, value interface{}) bool {
   192  		if first {
   193  			if !value.(*File).FileInfo().IsDir() {
   194  				return false
   195  			}
   196  			first = false
   197  		}
   198  		if onlyFile && value.(*File).FileInfo().IsDir() {
   199  			return true
   200  		}
   201  		name = key.(string)
   202  		if len(name) <= length {
   203  			return true
   204  		}
   205  		if path != name[:length] {
   206  			return false
   207  		}
   208  		// To avoid of, eg: /i18n and /i18n-dir
   209  		if !first && name[length] != '/' {
   210  			return true
   211  		}
   212  		if !recursive {
   213  			if strings.IndexByte(name[length+1:], '/') != -1 {
   214  				return true
   215  			}
   216  		}
   217  		for _, p := range patterns {
   218  			if match, err := filepath.Match(p, gfile.Basename(name)); err == nil && match {
   219  				files = append(files, value.(*File))
   220  				return true
   221  			}
   222  		}
   223  		return true
   224  	})
   225  	return files
   226  }
   227  
   228  // Dump prints the files of current resource object.
   229  func (r *Resource) Dump() {
   230  	var info os.FileInfo
   231  	r.tree.Iterator(func(key, value interface{}) bool {
   232  		info = value.(*File).FileInfo()
   233  		fmt.Printf("%v %7s %s\n", gtime.New(info.ModTime()).ISO8601(), gfile.FormatSize(info.Size()), key)
   234  		return true
   235  	})
   236  	fmt.Printf("TOTAL FILES: %d\n", r.tree.Size())
   237  }