Merge branch 'develop'

This commit is contained in:
John
2018-11-11 14:24:44 +08:00
15 changed files with 221 additions and 106 deletions

View File

@ -7,6 +7,7 @@
package gfile
import (
"gitee.com/johng/gf/g/os/gfpool"
"io"
"io/ioutil"
"os"
@ -15,8 +16,8 @@ import (
const (
// 方法中涉及到读取的时候的缓冲大小
gREAD_BUFFER = 1024
// 方法中涉及到文件指针池的默认缓存时间(秒)
gFILE_POOL_EXPIRE = 60
// 方法中涉及到文件指针池的默认缓存时间(秒)
gFILE_POOL_EXPIRE = 60000
)
// (文本)读取文件内容
@ -43,7 +44,7 @@ func putContents(path string, data []byte, flag int, perm int) error {
}
}
// 创建/打开文件
f, err := OpenWithFlagPerm(path, flag, perm)
f, err := gfpool.Open(path, flag, os.FileMode(perm), gFILE_POOL_EXPIRE)
if err != nil {
return err
}
@ -82,11 +83,11 @@ func PutBinContentsAppend(path string, content []byte) error {
}
// 获得文件内容下一个指定字节的位置
func GetNextCharOffset(file *os.File, char byte, start int64) int64 {
func GetNextCharOffset(reader io.ReaderAt, char byte, start int64) int64 {
buffer := make([]byte, gREAD_BUFFER)
offset := start
for {
if n, err := file.ReadAt(buffer, offset); n > 0 {
if n, err := reader.ReadAt(buffer, offset); n > 0 {
for i := 0; i < n; i++ {
if buffer[i] == char {
return int64(i) + offset
@ -102,7 +103,7 @@ func GetNextCharOffset(file *os.File, char byte, start int64) int64 {
// 获得文件内容下一个指定字节的位置
func GetNextCharOffsetByPath(path string, char byte, start int64) int64 {
if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil {
if f, err := gfpool.Open(path, os.O_RDONLY, gDEFAULT_PERM, gFILE_POOL_EXPIRE); err == nil {
defer f.Close()
return GetNextCharOffset(f, char, start)
} else {
@ -112,16 +113,16 @@ func GetNextCharOffsetByPath(path string, char byte, start int64) int64 {
}
// 获得文件内容直到下一个指定字节的位置(返回值包含该位置字符内容)
func GetBinContentsTilChar(file *os.File, char byte, start int64) ([]byte, int64) {
if offset := GetNextCharOffset(file, char, start); offset != -1 {
return GetBinContentsByTwoOffsets(file, start, offset + 1), offset
func GetBinContentsTilChar(reader io.ReaderAt, char byte, start int64) ([]byte, int64) {
if offset := GetNextCharOffset(reader, char, start); offset != -1 {
return GetBinContentsByTwoOffsets(reader, start, offset + 1), offset
}
return nil, -1
}
// 获得文件内容直到下一个指定字节的位置(返回值包含该位置字符内容)
func GetBinContentsTilCharByPath(path string, char byte, start int64) ([]byte, int64) {
if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil {
if f, err := gfpool.Open(path, os.O_RDONLY, gDEFAULT_PERM, gFILE_POOL_EXPIRE); err == nil {
defer f.Close()
return GetBinContentsTilChar(f, char, start)
} else {
@ -131,9 +132,9 @@ func GetBinContentsTilCharByPath(path string, char byte, start int64) ([]byte, i
}
// 获得文件内容中两个offset之间的内容 [start, end)
func GetBinContentsByTwoOffsets(file *os.File, start int64, end int64) []byte {
func GetBinContentsByTwoOffsets(reader io.ReaderAt, start int64, end int64) []byte {
buffer := make([]byte, end - start)
if _, err := file.ReadAt(buffer, start); err != nil {
if _, err := reader.ReadAt(buffer, start); err != nil {
return nil
}
return buffer
@ -141,7 +142,7 @@ func GetBinContentsByTwoOffsets(file *os.File, start int64, end int64) []byte {
// 获得文件内容中两个offset之间的内容 [start, end)
func GetBinContentsByTwoOffsetsByPath(path string, start int64, end int64) []byte {
if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil {
if f, err := gfpool.Open(path, os.O_RDONLY, gDEFAULT_PERM, gFILE_POOL_EXPIRE); err == nil {
defer f.Close()
return GetBinContentsByTwoOffsets(f, start, end)
} else {

View File

@ -28,7 +28,7 @@ type Pool struct {
// 文件指针池指针
type File struct {
os.File // 底层文件指针
*os.File // 底层文件指针
mu sync.RWMutex // 互斥锁
pool *Pool // 所属池
poolid int // 所属池ID如果池ID不同表示池已经重建那么该文件指针也应当销毁不能重新丢到原有的池中
@ -81,8 +81,8 @@ func newFilePool(p *Pool, path string, flag int, perm os.FileMode, expire int) *
if err != nil {
return nil, err
}
return &File{
File : *file,
return &File {
File : file,
pool : p,
poolid : p.id.Val(),
flag : flag,
@ -108,7 +108,7 @@ func (p *Pool) File() (*File, error) {
if file, err := os.OpenFile(f.path, f.flag, f.perm); err != nil {
return nil, err
} else {
f.File = *file
f.File = file
if stat, err = f.Stat(); err != nil {
return nil, err
}
@ -127,7 +127,9 @@ func (p *Pool) File() (*File, error) {
return nil, err
}
} else {
f.Seek(0, 0)
if _, err := f.Seek(0, 0); err != nil {
return nil, err
}
}
if !p.inited.Set(true) {
gfsnotify.Add(f.path, func(event *gfsnotify.Event) {

View File

@ -5,17 +5,44 @@ import (
"os"
)
func Benchmark_os_Open_Close(b *testing.B) {
func Benchmark_os_Open_Close_ALLFlags(b *testing.B) {
for i := 0; i < b.N; i++ {
f, _ := os.OpenFile("/tmp/bench-test", os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0766)
f, _ := os.OpenFile("/tmp/bench-test", os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666)
f.Close()
}
}
func Benchmark_gfpool_Open_Close(b *testing.B) {
func Benchmark_gfpool_Open_Close_ALLFlags(b *testing.B) {
for i := 0; i < b.N; i++ {
f, _ := Open("/tmp/bench-test", os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0766)
f, _ := Open("/tmp/bench-test", os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0666)
f.Close()
}
}
func Benchmark_os_Open_Close_RDWR(b *testing.B) {
for i := 0; i < b.N; i++ {
f, _ := os.OpenFile("/tmp/bench-test", os.O_RDWR, 0666)
f.Close()
}
}
func Benchmark_gfpool_Open_Close_RDWR(b *testing.B) {
for i := 0; i < b.N; i++ {
f, _ := Open("/tmp/bench-test", os.O_RDWR, 0666)
f.Close()
}
}
func Benchmark_os_Open_Close_RDONLY(b *testing.B) {
for i := 0; i < b.N; i++ {
f, _ := os.OpenFile("/tmp/bench-test", os.O_RDONLY, 0666)
f.Close()
}
}
func Benchmark_gfpool_Open_Close_RDONLY(b *testing.B) {
for i := 0; i < b.N; i++ {
f, _ := Open("/tmp/bench-test", os.O_RDONLY, 0666)
f.Close()
}
}

View File

@ -191,35 +191,75 @@ func (w *Watcher) startWatchLoop() {
// 检索给定path的回调方法**列表**
func (w *Watcher) getCallbacks(path string) *glist.List {
for path != "/" {
for {
if l := w.callbacks.Get(path); l != nil {
return l.(*glist.List)
} else {
path = fileDir(path)
if p := fileDir(path); p != path {
path = p
} else {
break
}
}
}
return nil
}
// 获得真正监听的文件路径,判断规则:
// 1、在 callbacks 中应当有回调注册函数(否则监听根本没意义)
// 2、如果该path下不存在回调注册函数则按照path长度从右往左递减直到减到目录地址为止(不包含)
// 3、如果仍旧无法匹配回调函数那么忽略否则使用查找到的新path覆盖掉event的path
func (w *Watcher) getWatchTruePath(path string) string {
if w.getCallbacks(path) != nil {
return path
}
dirPath := fileDir(path)
for {
path = path[0 : len(path) - 1]
if path == dirPath {
break
}
if w.getCallbacks(path) != nil {
return path
}
}
return ""
}
// 事件循环
func (w *Watcher) startEventLoop() {
go func() {
for {
if v := w.events.Pop(); v != nil {
event := v.(*Event)
// 如果是删除操作,那么需要判断是否文件真正不存在了
if event.IsRemove() {
if fileExists(event.Path) {
// 如果是文件删除事件,判断该文件是否存在,如果存在,那么将此事件认为“假删除”,
// 并重新添加监控(底层fsnotify会自动删除掉监控这里重新添加回去)
w.watcher.Add(event.Path)
// 修改事件操作为重命名(相当于重命名为自身名称,最终名称没变)
event.Op = RENAME
} else {
// 如果是真实删除,那么递归删除监控信息
w.Remove(event.Path)
}
if path := w.getWatchTruePath(event.Path); path == "" {
continue
} else {
event.Path = path
}
switch {
// 如果是删除操作,那么需要判断是否文件真正不存在了,如果存在,那么将此事件认为“假删除”
case event.IsRemove():
if fileExists(event.Path) {
// 重新添加监控(底层fsnotify会自动删除掉监控这里重新添加回去)
// 注意这里调用的是底层fsnotify添加监控只会产生回调事件并不会使回调函数重复注册
w.watcher.Add(event.Path)
// 修改事件操作为重命名(相当于重命名为自身名称,最终名称没变)
event.Op = RENAME
} else {
// 如果是真实删除,那么递归删除监控信息
w.Remove(event.Path)
}
// 如果是删除操作,那么需要判断是否文件真正不存在了,如果存在,那么将此事件认为“假命名”
// (特别是某些编辑器在编辑文件时会先对文件RENAME再CHMOD)
case event.IsRename():
if fileExists(event.Path) {
// 重新添加监控
w.watcher.Add(event.Path)
}
}
callbacks := w.getCallbacks(event.Path)
// 如果创建了新的目录,那么将这个目录递归添加到监控中
if event.IsCreate() && fileIsDir(event.Path) {

View File

@ -11,6 +11,7 @@ import (
"fmt"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/gfpool"
"gitee.com/johng/gf/g/os/gmlock"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gregex"
@ -38,6 +39,8 @@ type Logger struct {
const (
gDEFAULT_FILE_FORMAT = `{Y-m-d}.log`
gDEFAULT_FILE_POOL_FLAGS = os.O_CREATE|os.O_WRONLY|os.O_APPEND
gDEFAULT_FPOOL_PERM = os.FileMode(0666)
gDEFAULT_FPOOL_EXPIRE = 60000
)
var (
@ -127,14 +130,14 @@ func (l *Logger) GetWriter() io.Writer {
}
// 获取默认的文件IO
func (l *Logger) getFilePointer() *os.File {
func (l *Logger) getFilePointer() *gfpool.File {
if path := l.path.Val(); path != "" {
// 文件名称中使用"{}"包含的内容使用gtime格式化
file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file.Val(), func(s string) string {
return gtime.Now().Format(strings.Trim(s, "{}"))
})
fpath := path + gfile.Separator + file
if fp, err := gfile.OpenWithFlagPerm(fpath, gDEFAULT_FILE_POOL_FLAGS, 0666); err == nil {
if fp, err := gfpool.Open(fpath, gDEFAULT_FILE_POOL_FLAGS, gDEFAULT_FPOOL_PERM, gDEFAULT_FPOOL_EXPIRE); err == nil {
return fp
} else {
fmt.Fprintln(os.Stderr, err)

View File

@ -0,0 +1,20 @@
package main
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/download", func(r *ghttp.Request){
r.Response.Header().Set("Content-Type", "text/html;charset=utf-8");
r.Response.Header().Set("Content-type", "application/force-download");
r.Response.Header().Set("Content-Type", "application/octet-stream");
r.Response.Header().Set("Accept-Ranges", "bytes");
r.Response.Header().Set("Content-Disposition", "attachment;filename=\"下载文件名称.txt\"");
r.Response.ServeFile("text.txt")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1 @@
下载文件内容。

View File

@ -1,16 +0,0 @@
package main
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request){
content :=`{{if (get "name")}} {{get "name"}} {{else}} NoName {{end}}`
r.Response.WriteTplContent(content, nil)
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
</head>
<body>
<H1>姓名 {{.name}}</H1>
12
</body>
</html>

View File

@ -0,0 +1,23 @@
package main
import (
"gitee.com/johng/gf/g/frame/gmvc"
"gitee.com/johng/gf/g/net/ghttp"
)
type ControllerIndex struct {
gmvc.Controller
}
func (c *ControllerIndex) Info() {
c.View.Assign("title", "Go Frame 第一个网站")
c.View.Assigns(map[string]interface{}{
"name" : "很开心1",
"score" : 100,
})
c.View.Display("index.html")
}
func main() {
s := ghttp.GetServer()
s.BindController("/", new(ControllerIndex))
s.SetPort(8199)
s.Run()
}

View File

@ -3,6 +3,7 @@ package main
import (
"gitee.com/johng/gf/third/github.com/fsnotify/fsnotify"
"log"
"gitee.com/johng/gf/g/os/glog"
)
func main() {
@ -13,7 +14,7 @@ func main() {
}
defer watch.Close()
//添加要监控的对象,文件或文件夹
err = watch.Add("/home/john/temp")
err = watch.Add("D:\\Workspace\\Go\\GOPATH\\src\\gitee.com\\johng\\gf\\geg\\other\\test.go")
if err != nil {
log.Fatal(err)
}
@ -21,32 +22,12 @@ func main() {
go func() {
for {
select {
case ev := <-watch.Events:
//判断事件发生的类型如下5种
// Create 创建
// Write 写入
// Remove 删除
// Rename 重命名
// Chmod 修改权限
if ev.Op&fsnotify.Create == fsnotify.Create {
log.Println("创建文件 : ", ev.Name)
}
if ev.Op&fsnotify.Write == fsnotify.Write {
log.Println("写入文件 : ", ev.Name)
}
if ev.Op&fsnotify.Remove == fsnotify.Remove {
log.Println("删除文件 : ", ev.Name)
}
if ev.Op&fsnotify.Rename == fsnotify.Rename {
log.Println("重命名文件 : ", ev.Name)
}
if ev.Op&fsnotify.Chmod == fsnotify.Chmod {
log.Println("修改权限 : ", ev.Name)
}
case ev := <-watch.Events:
glog.Println(ev)
case err := <-watch.Errors:
log.Println("error : ", err)
return
case err := <-watch.Errors:
log.Println("error : ", err)
return
}
}

View File

@ -6,29 +6,13 @@ import (
)
func main() {
// /home/john/temp 是一个目录,当然也可以指定文件
path := "/home/john/temp"
path := "D:\\Workspace\\Go\\GOPATH\\src\\gitee.com\\johng\\gf\\geg\\other\\test.go"
_, err := gfsnotify.Add(path, func(event *gfsnotify.Event) {
if event.IsCreate() {
glog.Println("创建文件 : ", event.Path)
}
if event.IsWrite() {
glog.Println("写入文件 : ", event.Path)
}
if event.IsRemove() {
glog.Println("删除文件 : ", event.Path)
}
if event.IsRename() {
glog.Println("重命名文件 : ", event.Path)
}
if event.IsChmod() {
glog.Println("修改权限 : ", event.Path)
}
glog.Println(event)
})
// 移除对该path的监听
gfsnotify.Remove(path)
//gfsnotify.Remove(path)
if err != nil {
glog.Fatalln(err)

View File

@ -0,0 +1,19 @@
package main
import (
"gitee.com/johng/gf/g/os/gfsnotify"
"gitee.com/johng/gf/g/os/glog"
)
// 对同一个文件多次Add是否超过系统inotify限制
func main() {
path := "/Users/john/temp/log"
for i := 0; i < 9999999; i++ {
_, err := gfsnotify.Add(path, func(event *gfsnotify.Event) {
glog.Println(event)
})
if err != nil {
glog.Fatalln(err)
}
}
}

View File

@ -2,15 +2,9 @@ package main
import (
"fmt"
"os"
"time"
"gitee.com/johng/gf/g/frame/gins"
)
func main() {
for {
stat, err := os.Stat("/home/john/temp/log")
fmt.Println(err)
fmt.Println(stat.Size())
time.Sleep(time.Second)
}
fmt.Print(gins.Config().GetFilePath())
}

View File

@ -0,0 +1,25 @@
package main
import (
"gitee.com/johng/gf/g/util/gutil"
"gitee.com/johng/gf/g/util/gvalid"
)
func main() {
type User struct {
Name string `gvalid:"name @required|length:6,30#请输入用户名称|用户名称长度非法"`
Pass1 string `gvalid:"password1@required|password3"`
Pass2 string `gvalid:"password2@required|password3|same:password1#||两次密码不一致,请重新输入"`
}
user := &User{
Name : "john",
Pass1: "Abc123!@#",
Pass2: "123",
}
err := gvalid.CheckStruct(user, nil)
gutil.Dump(err)
gutil.Dump(err.String())
gutil.Dump(err.FirstString())
}