diff --git a/.gitignore b/.gitignore index 143e3c3c5..bec9782b8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ cbuild cmd/gf/main cmd/gf/gf go.work +temp/ diff --git a/cmd/gf/internal/cmd/cmd_build.go b/cmd/gf/internal/cmd/cmd_build.go index 0b72b496b..5a72e4d76 100644 --- a/cmd/gf/internal/cmd/cmd_build.go +++ b/cmd/gf/internal/cmd/cmd_build.go @@ -204,7 +204,9 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e mlog.Printf(`remove the automatically generated resource go file: %s`, in.PackDst) }() } - packCmd := fmt.Sprintf(`gf pack %s %s`, in.PackSrc, in.PackDst) + // remove black space in separator. + in.PackSrc, _ = gregex.ReplaceString(`,\s+`, `,`, in.PackSrc) + packCmd := fmt.Sprintf(`gf pack %s %s --keepPath=true`, in.PackSrc, in.PackDst) mlog.Print(packCmd) gproc.MustShellRun(ctx, packCmd) } diff --git a/cmd/gf/internal/cmd/cmd_pack.go b/cmd/gf/internal/cmd/cmd_pack.go index a54905837..c60c8e6ea 100644 --- a/cmd/gf/internal/cmd/cmd_pack.go +++ b/cmd/gf/internal/cmd/cmd_pack.go @@ -37,28 +37,31 @@ gf pack /var/www/public packed/data.go -n=packed destination file path for packed file. if extension of the filename is ".go" and "-n" option is given, it enables packing SRC to go file, or else it packs SRC into a binary file. ` - cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed` - cPackPrefixBrief = `prefix for each file packed into the resource file` + cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed` + cPackPrefixBrief = `prefix for each file packed into the resource file` + cPackKeepPathBrief = `keep the source path from system to resource file, usually for relative path` ) func init() { gtag.Sets(g.MapStrStr{ - `cPackUsage`: cPackUsage, - `cPackBrief`: cPackBrief, - `cPackEg`: cPackEg, - `cPackSrcBrief`: cPackSrcBrief, - `cPackDstBrief`: cPackDstBrief, - `cPackNameBrief`: cPackNameBrief, - `cPackPrefixBrief`: cPackPrefixBrief, + `cPackUsage`: cPackUsage, + `cPackBrief`: cPackBrief, + `cPackEg`: cPackEg, + `cPackSrcBrief`: cPackSrcBrief, + `cPackDstBrief`: cPackDstBrief, + `cPackNameBrief`: cPackNameBrief, + `cPackPrefixBrief`: cPackPrefixBrief, + `cPackKeepPathBrief`: cPackKeepPathBrief, }) } type cPackInput struct { - g.Meta `name:"pack"` - Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"` - Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"` - Name string `name:"name" short:"n" brief:"{cPackNameBrief}"` - Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"` + g.Meta `name:"pack"` + Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"` + Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"` + Name string `name:"name" short:"n" brief:"{cPackNameBrief}"` + Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"` + KeepPath bool `name:"keepPath" short:"k" brief:"{cPackKeepPathBrief}" orphan:"true"` } type cPackOutput struct{} @@ -75,12 +78,16 @@ func (c cPack) Index(ctx context.Context, in cPackInput) (out *cPackOutput, err if in.Name == "" && gfile.ExtName(in.Dst) == "go" { in.Name = gfile.Basename(gfile.Dir(in.Dst)) } + var option = gres.Option{ + Prefix: in.Prefix, + KeepPath: in.KeepPath, + } if in.Name != "" { - if err = gres.PackToGoFile(in.Src, in.Dst, in.Name, in.Prefix); err != nil { + if err = gres.PackToGoFileWithOption(in.Src, in.Dst, in.Name, option); err != nil { mlog.Fatalf("pack failed: %v", err) } } else { - if err = gres.PackToFile(in.Src, in.Dst, in.Prefix); err != nil { + if err = gres.PackToFileWithOption(in.Src, in.Dst, option); err != nil { mlog.Fatalf("pack failed: %v", err) } } diff --git a/example/pack/hack/config.yaml b/example/pack/hack/config.yaml new file mode 100644 index 000000000..9dec998f1 --- /dev/null +++ b/example/pack/hack/config.yaml @@ -0,0 +1,9 @@ +gfcli: + build: + name: "pack" + arch: "amd64" + system: "darwin" + packSrc: "resource/public,manifest/i18n" + packDst: "packed/build_packed_data.go" + mod: "" + cgo: 0 \ No newline at end of file diff --git a/example/pack/main.go b/example/pack/main.go new file mode 100644 index 000000000..28256030d --- /dev/null +++ b/example/pack/main.go @@ -0,0 +1,28 @@ +package main + +import ( + _ "github.com/gogf/gf/example/pack/packed" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/i18n/gi18n" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gres" +) + +func main() { + gres.Dump() + + s := g.Server() + s.SetPort(8199) + s.SetServerRoot("resource/public") + s.BindHandler("/i18n", func(r *ghttp.Request) { + var ( + lang = r.Get("lang", "zh-CN").String() + ctx = gi18n.WithLanguage(r.Context(), lang) + content string + ) + content = g.I18n().T(ctx, `{#hello} {#world}!`) + r.Response.Write(content) + }) + s.Run() +} diff --git a/example/pack/manifest/i18n/ja.yaml b/example/pack/manifest/i18n/ja.yaml new file mode 100644 index 000000000..a8abe1be1 --- /dev/null +++ b/example/pack/manifest/i18n/ja.yaml @@ -0,0 +1,2 @@ +hello: "こんにちは" +world: "世界" \ No newline at end of file diff --git a/example/pack/manifest/i18n/ru.yaml b/example/pack/manifest/i18n/ru.yaml new file mode 100644 index 000000000..182ec5eec --- /dev/null +++ b/example/pack/manifest/i18n/ru.yaml @@ -0,0 +1,2 @@ +hello: "Привет" +world: "мир" \ No newline at end of file diff --git a/example/pack/manifest/i18n/zh-CN.yaml b/example/pack/manifest/i18n/zh-CN.yaml new file mode 100644 index 000000000..38f96cc1f --- /dev/null +++ b/example/pack/manifest/i18n/zh-CN.yaml @@ -0,0 +1,2 @@ +hello: "你好" +world: "世界" \ No newline at end of file diff --git a/example/pack/packed/paked.go b/example/pack/packed/paked.go new file mode 100644 index 000000000..e20ab1e95 --- /dev/null +++ b/example/pack/packed/paked.go @@ -0,0 +1 @@ +package packed diff --git a/example/pack/resource/public/index.html b/example/pack/resource/public/index.html new file mode 100644 index 000000000..6d1907228 --- /dev/null +++ b/example/pack/resource/public/index.html @@ -0,0 +1,8 @@ + + + Hello World + + +

Hello World!

+ + \ No newline at end of file diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index 94aee52cc..4492cbb10 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -39,8 +39,14 @@ type Options struct { } var ( - defaultLanguage = "en" // defaultDelimiters defines the default language if user does not specified in options. - defaultDelimiters = []string{"{#", "}"} // defaultDelimiters defines the default key variable delimiters. + // defaultDelimiters defines the default language if user does not specify in options. + defaultLanguage = "en" + + // defaultDelimiters defines the default key variable delimiters. + defaultDelimiters = []string{"{#", "}"} + + // i18n files searching folders. + searchFolders = []string{"manifest/i18n", "manifest/config/i18n", "i18n"} ) // New creates and returns a new i18n manager. @@ -73,13 +79,15 @@ func New(options ...Options) *Manager { // DefaultOptions creates and returns a default options for i18n manager. func DefaultOptions() Options { - var ( - path = "i18n" - realPath, _ = gfile.Search(path) - ) - if realPath != "" { - path = realPath - // To avoid of the source path of GF: github.com/gogf/i18n/gi18n + var path string + for _, folder := range searchFolders { + path, _ = gfile.Search(folder) + if path != "" { + break + } + } + if path != "" { + // To avoid of the source path of GoFrame: github.com/gogf/i18n/gi18n if gfile.Exists(path + gfile.Separator + "gi18n") { path = "" } diff --git a/os/gres/gres_func.go b/os/gres/gres_func.go index 9e302a734..d2bc6abd6 100644 --- a/os/gres/gres_func.go +++ b/os/gres/gres_func.go @@ -33,20 +33,33 @@ func init() { ` ) +// Option contains the extra options for Pack functions. +type Option struct { + Prefix string // The file path prefix for each file item in resource manager. + KeepPath bool // Keep the passed path when packing, usually for relative path. +} + // Pack packs the path specified by `srcPaths` into bytes. // The unnecessary parameter `keyPrefix` indicates the prefix for each file // packed into the result bytes. // // Note that parameter `srcPaths` supports multiple paths join with ','. +// +// Deprecated, use PackWithOption instead. func Pack(srcPaths string, keyPrefix ...string) ([]byte, error) { - var ( - buffer = bytes.NewBuffer(nil) - headerPrefix = "" - ) + option := Option{} if len(keyPrefix) > 0 && keyPrefix[0] != "" { - headerPrefix = keyPrefix[0] + option.Prefix = keyPrefix[0] } - err := zipPathWriter(srcPaths, buffer, headerPrefix) + return PackWithOption(srcPaths, option) +} + +// PackWithOption packs the path specified by `srcPaths` into bytes. +// +// Note that parameter `srcPaths` supports multiple paths join with ','. +func PackWithOption(srcPaths string, option Option) ([]byte, error) { + var buffer = bytes.NewBuffer(nil) + err := zipPathWriter(srcPaths, buffer, option) if err != nil { return nil, err } @@ -59,6 +72,8 @@ func Pack(srcPaths string, keyPrefix ...string) ([]byte, error) { // packed into the result bytes. // // Note that parameter `srcPaths` supports multiple paths join with ','. +// +// Deprecated, use PackToFileWithOption instead. func PackToFile(srcPaths, dstPath string, keyPrefix ...string) error { data, err := Pack(srcPaths, keyPrefix...) if err != nil { @@ -67,6 +82,17 @@ func PackToFile(srcPaths, dstPath string, keyPrefix ...string) error { return gfile.PutBytes(dstPath, data) } +// PackToFileWithOption packs the path specified by `srcPaths` to target file `dstPath`. +// +// Note that parameter `srcPaths` supports multiple paths join with ','. +func PackToFileWithOption(srcPaths, dstPath string, option Option) error { + data, err := PackWithOption(srcPaths, option) + if err != nil { + return err + } + return gfile.PutBytes(dstPath, data) +} + // PackToGoFile packs the path specified by `srcPaths` to target go file `goFilePath` // with given package name `pkgName`. // @@ -74,6 +100,8 @@ func PackToFile(srcPaths, dstPath string, keyPrefix ...string) error { // packed into the result bytes. // // Note that parameter `srcPaths` supports multiple paths join with ','. +// +// Deprecated, use PackToGoFileWithOption instead. func PackToGoFile(srcPath, goFilePath, pkgName string, keyPrefix ...string) error { data, err := Pack(srcPath, keyPrefix...) if err != nil { @@ -85,6 +113,21 @@ func PackToGoFile(srcPath, goFilePath, pkgName string, keyPrefix ...string) erro ) } +// PackToGoFileWithOption packs the path specified by `srcPaths` to target go file `goFilePath` +// with given package name `pkgName`. +// +// Note that parameter `srcPaths` supports multiple paths join with ','. +func PackToGoFileWithOption(srcPath, goFilePath, pkgName string, option Option) error { + data, err := PackWithOption(srcPath, option) + if err != nil { + return err + } + return gfile.PutContents( + goFilePath, + fmt.Sprintf(gstr.TrimLeft(packedGoSourceTemplate), pkgName, gbase64.EncodeToString(data)), + ) +} + // Unpack unpacks the content specified by `path` to []*File. func Unpack(path string) ([]*File, error) { realPath, err := gfile.Search(path) diff --git a/os/gres/gres_func_zip.go b/os/gres/gres_func_zip.go index cb3ecb045..23bf17f0f 100644 --- a/os/gres/gres_func_zip.go +++ b/os/gres/gres_func_zip.go @@ -8,7 +8,6 @@ package gres import ( "archive/zip" - "context" "io" "os" "strings" @@ -16,7 +15,6 @@ import ( "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/internal/fileinfo" - "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/text/gregex" ) @@ -26,12 +24,12 @@ import ( // // Note that the parameter `paths` can be either a directory or a file, which // supports multiple paths join with ','. -func zipPathWriter(paths string, writer io.Writer, prefix ...string) error { +func zipPathWriter(paths string, writer io.Writer, option ...Option) error { zipWriter := zip.NewWriter(writer) defer zipWriter.Close() for _, path := range strings.Split(paths, ",") { path = strings.TrimSpace(path) - if err := doZipPathWriter(path, "", zipWriter, prefix...); err != nil { + if err := doZipPathWriter(path, zipWriter, option...); err != nil { return err } } @@ -42,41 +40,53 @@ func zipPathWriter(paths string, writer io.Writer, prefix ...string) error { // The parameter `exclude` specifies the exclusive file path that is not compressed to `zipWriter`, // commonly the destination zip file path. // The unnecessary parameter `prefix` indicates the path prefix for zip file. -func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix ...string) error { +func doZipPathWriter(srcPath string, zipWriter *zip.Writer, option ...Option) error { var ( - err error - files []string + err error + files []string + usedOption Option + absolutePath string ) - path, err = gfile.Search(path) + if len(option) > 0 { + usedOption = option[0] + } + absolutePath, err = gfile.Search(srcPath) if err != nil { return err } - if gfile.IsDir(path) { - files, err = gfile.ScanDir(path, "*", true) + if gfile.IsDir(absolutePath) { + files, err = gfile.ScanDir(absolutePath, "*", true) if err != nil { return err } } else { - files = []string{path} + files = []string{absolutePath} } - headerPrefix := "" - if len(prefix) > 0 && prefix[0] != "" { - headerPrefix = prefix[0] - } - headerPrefix = strings.TrimRight(headerPrefix, `\/`) - if len(headerPrefix) > 0 && gfile.IsDir(path) { + headerPrefix := strings.TrimRight(usedOption.Prefix, `\/`) + if headerPrefix != "" && gfile.IsDir(absolutePath) { headerPrefix += "/" } + if headerPrefix == "" { - headerPrefix = gfile.Basename(path) + if usedOption.KeepPath { + // It keeps the path from file system to zip info in resource manager. + // Usually for relative path, it makes little sense for absolute path. + headerPrefix = srcPath + } else { + headerPrefix = gfile.Basename(absolutePath) + } } headerPrefix = strings.Replace(headerPrefix, `//`, `/`, -1) for _, file := range files { - if exclude == file { - intlog.Printf(context.TODO(), `exclude file path: %s`, file) - continue - } - subFilePath := file[len(path):] + // It here calculates the file name prefix, especially packing the directory. + // Eg: + // path: dir1 + // file: dir1/dir2/file + // file[len(absolutePath):] => /dir2/file + // gfile.Dir(subFilePath) => /dir2 + var subFilePath string + // Normal handling: remove the `absolutePath`(source directory path) for file. + subFilePath = file[len(absolutePath):] if subFilePath != "" { subFilePath = gfile.Dir(subFilePath) } @@ -86,17 +96,20 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix } // Add all directories to zip archive. if headerPrefix != "" { - var name string - path = headerPrefix + var ( + name string + tmpPath = headerPrefix + ) for { - name = strings.Replace(gfile.Basename(path), `\`, `/`, -1) - if err = zipFileVirtual(fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), path, zipWriter); err != nil { + name = strings.Replace(gfile.Basename(tmpPath), `\`, `/`, -1) + err = zipFileVirtual(fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), tmpPath, zipWriter) + if err != nil { return err } - if path == `/` || !strings.Contains(path, `/`) { + if tmpPath == `/` || !strings.Contains(tmpPath, `/`) { break } - path = gfile.Dir(path) + tmpPath = gfile.Dir(tmpPath) } } return nil @@ -106,24 +119,29 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix // The parameter `prefix` indicates the path prefix for zip file. func zipFile(path string, prefix string, zw *zip.Writer) error { prefix = strings.Replace(prefix, `//`, `/`, -1) + file, err := os.Open(path) if err != nil { - err = gerror.Wrapf(err, `os.Open failed for file "%s"`, path) + err = gerror.Wrapf(err, `os.Open failed for path "%s"`, path) return nil } defer file.Close() + info, err := file.Stat() if err != nil { - err = gerror.Wrapf(err, `read file stat failed for file "%s"`, path) + err = gerror.Wrapf(err, `read file stat failed for path "%s"`, path) return err } + header, err := createFileHeader(info, prefix) if err != nil { return err } if !info.IsDir() { + // Default compression level. header.Method = zip.Deflate } + // Zip header containing the info of a zip file. writer, err := zw.CreateHeader(header) if err != nil { err = gerror.Wrapf(err, `create zip header failed for %#v`, header) diff --git a/os/gres/gres_resource.go b/os/gres/gres_resource.go index 1289246d8..0e45eca75 100644 --- a/os/gres/gres_resource.go +++ b/os/gres/gres_resource.go @@ -277,7 +277,7 @@ func (r *Resource) Dump() { r.tree.Iterator(func(key, value interface{}) bool { info = value.(*File).FileInfo() fmt.Printf( - "%v %7s %s\n", + "%v %8s %s\n", gtime.New(info.ModTime()).ISO8601(), gfile.FormatSize(info.Size()), key,