mirror of https://github.com/q191201771/naza
[feat] package filesystemlayer: 新的包,提供一层文件操作的抽象,可以使用内存替换磁盘作为存储
parent
2e84251f0f
commit
ee31064541
@ -0,0 +1,49 @@
|
||||
// Copyright 2021, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/naza
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package filesystemlayer
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
type FSLDisk struct {
|
||||
}
|
||||
|
||||
func (f *FSLDisk) Type() FSLType {
|
||||
return FSLTypeDisk
|
||||
}
|
||||
|
||||
func (f *FSLDisk) Create(name string) (IFile, error) {
|
||||
return os.Create(name)
|
||||
}
|
||||
|
||||
func (f *FSLDisk) Rename(oldpath string, newpath string) error {
|
||||
return os.Rename(oldpath, newpath)
|
||||
}
|
||||
|
||||
func (f *FSLDisk) MkdirAll(path string, perm uint32) error {
|
||||
return os.MkdirAll(path, os.FileMode(perm))
|
||||
}
|
||||
|
||||
func (f *FSLDisk) Remove(name string) error {
|
||||
return os.Remove(name)
|
||||
}
|
||||
|
||||
func (f *FSLDisk) RemoveAll(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
func (f *FSLDisk) ReadFile(filename string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filename)
|
||||
}
|
||||
|
||||
func (f *FSLDisk) WriteFile(filename string, data []byte, perm uint32) error {
|
||||
return ioutil.WriteFile(filename, data, os.FileMode(perm))
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// Copyright 2021, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/naza
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package filesystemlayer
|
||||
|
||||
import "os"
|
||||
|
||||
var _ IFileSystemLayer = &FSLDisk{}
|
||||
var _ IFileSystemLayer = &FSLMemory{}
|
||||
|
||||
var _ IFile = &os.File{}
|
||||
var _ IFile = &file{}
|
@ -0,0 +1,112 @@
|
||||
// Copyright 2021, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/naza
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package filesystemlayer_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/q191201771/naza/pkg/nazalog"
|
||||
|
||||
"github.com/q191201771/naza/pkg/assert"
|
||||
|
||||
"github.com/q191201771/naza/pkg/filesystemlayer"
|
||||
)
|
||||
|
||||
func TestCase1(t *testing.T) {
|
||||
fslCtx := filesystemlayer.FSLFactory(filesystemlayer.FSLTypeMemory)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(16)
|
||||
|
||||
for i := 0; i < 16; i++ {
|
||||
go func(ii int) {
|
||||
dir := fmt.Sprintf("/tmp/lal/hls/test%d", ii)
|
||||
err := fslCtx.MkdirAll(dir, 0777)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
for j := 0; j < 32; j++ {
|
||||
filename := fmt.Sprintf("/tmp/lal/hls/test%d/%d.ts", ii, j)
|
||||
nazalog.Infof("%d %d %s", ii, j, filename)
|
||||
fp, err := fslCtx.Create(filename)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
n, err := fp.Write([]byte("hello"))
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, 5, n)
|
||||
|
||||
n, err = fp.Write([]byte("world"))
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, 5, n)
|
||||
|
||||
err = fp.Close()
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// 正常读
|
||||
b, err := fslCtx.ReadFile("/tmp/lal/hls/test1/1.ts")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, []byte("helloworld"), b)
|
||||
|
||||
// 删文件
|
||||
err = fslCtx.Remove("/tmp/lal/hls/test1/1.ts")
|
||||
assert.Equal(t, nil, err)
|
||||
b, err = fslCtx.ReadFile("/tmp/lal/hls/test1/1.ts")
|
||||
assert.Equal(t, filesystemlayer.ErrNotFound, err)
|
||||
assert.Equal(t, nil, b)
|
||||
|
||||
// 正常读
|
||||
b, err = fslCtx.ReadFile("/tmp/lal/hls/test2/2.ts")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, []byte("helloworld"), b)
|
||||
|
||||
// 文件重命名
|
||||
err = fslCtx.Rename("/tmp/lal/hls/test2/2.ts", "/tmp/lal/hls/test2/new2.ts")
|
||||
b, err = fslCtx.ReadFile("/tmp/lal/hls/test2/2.ts")
|
||||
assert.Equal(t, filesystemlayer.ErrNotFound, err)
|
||||
assert.Equal(t, nil, b)
|
||||
b, err = fslCtx.ReadFile("/tmp/lal/hls/test2/new2.ts")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, []byte("helloworld"), b)
|
||||
|
||||
// 删文件夹
|
||||
err = fslCtx.RemoveAll("/tmp/lal/hls/test1")
|
||||
assert.Equal(t, nil, err)
|
||||
b, err = fslCtx.ReadFile("/tmp/lal/hls/test1/1.ts")
|
||||
assert.Equal(t, filesystemlayer.ErrNotFound, err)
|
||||
assert.Equal(t, nil, b)
|
||||
|
||||
// 创建已经存在的文件
|
||||
fp, err := fslCtx.Create("/tmp/lal/hls/test3/3.ts")
|
||||
assert.Equal(t, nil, err)
|
||||
b, err = fslCtx.ReadFile("/tmp/lal/hls/test3/3.ts")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, nil, b)
|
||||
n, err := fp.Write([]byte("asd"))
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, 3, n)
|
||||
b, err = fslCtx.ReadFile("/tmp/lal/hls/test3/3.ts")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, []byte("asd"), b)
|
||||
|
||||
// 删除不存在的文件
|
||||
err = fslCtx.Remove("/tmp/lal/hls/test1/1.ts")
|
||||
assert.Equal(t, filesystemlayer.ErrNotFound, err)
|
||||
|
||||
// 重命名不存在的文件
|
||||
err = fslCtx.Rename("/tmp/lal/hls/test1/1.ts", "/tmp/lal/hls/test1/new1.ts")
|
||||
assert.Equal(t, filesystemlayer.ErrNotFound, err)
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
// Copyright 2021, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/naza
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package filesystemlayer
|
||||
|
||||
//var global IFileSystemLayer
|
||||
//
|
||||
//func Config(t FSLType) {
|
||||
// global = FSLFactory(t)
|
||||
//}
|
||||
//
|
||||
//func Type() FSLType {
|
||||
// return global.Type()
|
||||
//}
|
||||
//
|
||||
//func Create(name string) (IFile, error) {
|
||||
// return global.Create(name)
|
||||
//}
|
||||
//func Rename(oldpath string, newpath string) error {
|
||||
// return global.Rename(oldpath, newpath)
|
||||
//}
|
||||
//
|
||||
//func MkdirAll(path string, perm uint32) error {
|
||||
// return MkdirAll(path, perm)
|
||||
//}
|
||||
//
|
||||
//func Remove(name string) error {
|
||||
// return Remove(name)
|
||||
//}
|
||||
//
|
||||
//func RemoveAll(path string) error {
|
||||
// return RemoveAll(path)
|
||||
//}
|
||||
//
|
||||
//func ReadFile(filename string) ([]byte, error) {
|
||||
// return global.ReadFile(filename)
|
||||
//}
|
||||
//
|
||||
//func WriteFile(filename string, data []byte, perm uint32) error {
|
||||
// return global.WriteFile(filename, data, perm)
|
||||
//}
|
||||
//
|
||||
//func init() {
|
||||
// global = FSLFactory(FSLTypeDisk)
|
||||
//}
|
@ -0,0 +1,144 @@
|
||||
// Copyright 2021, Chef. All rights reserved.
|
||||
// https://github.com/q191201771/naza
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style license
|
||||
// that can be found in the License file.
|
||||
//
|
||||
// Author: Chef (191201771@qq.com)
|
||||
|
||||
package filesystemlayer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("naza filesystemlayer: not found")
|
||||
|
||||
type FSLMemory struct {
|
||||
mu sync.Mutex
|
||||
files map[string]*file // key filename
|
||||
}
|
||||
|
||||
type file struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func NewFSLMemory() *FSLMemory {
|
||||
return &FSLMemory{
|
||||
files: make(map[string]*file),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FSLMemory) Type() FSLType {
|
||||
return FSLTypeMemory
|
||||
}
|
||||
|
||||
func (f *FSLMemory) Create(name string) (IFile, error) {
|
||||
return f.openFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
}
|
||||
|
||||
func (f *FSLMemory) Rename(oldpath string, newpath string) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
fi, exist := f.files[oldpath]
|
||||
if !exist {
|
||||
return ErrNotFound
|
||||
}
|
||||
delete(f.files, oldpath)
|
||||
f.files[newpath] = fi
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FSLMemory) MkdirAll(path string, perm uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FSLMemory) Remove(name string) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
_, exist := f.files[name]
|
||||
if !exist {
|
||||
return ErrNotFound
|
||||
}
|
||||
delete(f.files, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FSLMemory) RemoveAll(path string) error {
|
||||
if !os.IsPathSeparator(path[len(path)-1]) {
|
||||
path = fmt.Sprintf("%s%c", path, os.PathSeparator)
|
||||
}
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
files := make(map[string]*file)
|
||||
for k, v := range f.files {
|
||||
if !strings.HasPrefix(k, path) {
|
||||
files[k] = v
|
||||
}
|
||||
}
|
||||
f.files = files
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FSLMemory) ReadFile(filename string) ([]byte, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
fi, exist := f.files[filename]
|
||||
if !exist {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return fi.clone(), nil
|
||||
}
|
||||
|
||||
func (f *FSLMemory) WriteFile(filename string, data []byte, perm uint32) error {
|
||||
fi, err := f.openFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fi.Write(data)
|
||||
if err1 := fi.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *FSLMemory) openFile(name string, flag int, perm uint32) (IFile, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
fi, ok := f.files[name]
|
||||
if !ok {
|
||||
fi = &file{}
|
||||
f.files[name] = fi
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
fi.truncate()
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
func (f *file) Write(b []byte) (n int, err error) {
|
||||
f.buf = append(f.buf, b...)
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (f *file) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *file) truncate() {
|
||||
f.buf = nil
|
||||
}
|
||||
|
||||
func (f *file) clone() []byte {
|
||||
if f.buf == nil {
|
||||
return nil
|
||||
}
|
||||
b := make([]byte, len(f.buf))
|
||||
copy(b, f.buf)
|
||||
return b
|
||||
}
|
Loading…
Reference in New Issue