2020-05-11 15:08:27 +03:00
|
|
|
package file
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
2020-07-27 15:22:00 +03:00
|
|
|
"github.com/micro/go-micro/v3/errors"
|
|
|
|
"github.com/micro/go-micro/v3/logger"
|
|
|
|
"github.com/micro/go-micro/v3/server"
|
|
|
|
proto "github.com/micro/go-micro/v3/util/file/proto"
|
2020-05-11 15:08:27 +03:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewHandler is a handler that can be registered with a micro Server
|
|
|
|
func NewHandler(readDir string) proto.FileHandler {
|
|
|
|
return &handler{
|
|
|
|
readDir: readDir,
|
|
|
|
session: &session{
|
|
|
|
files: make(map[int64]*os.File),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterHandler is a convenience method for registering a handler
|
|
|
|
func RegisterHandler(s server.Server, readDir string) {
|
|
|
|
proto.RegisterFileHandler(s, NewHandler(readDir))
|
|
|
|
}
|
|
|
|
|
|
|
|
type handler struct {
|
|
|
|
readDir string
|
|
|
|
session *session
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) Open(ctx context.Context, req *proto.OpenRequest, rsp *proto.OpenResponse) error {
|
|
|
|
path := filepath.Join(h.readDir, req.Filename)
|
2020-05-13 13:07:53 +03:00
|
|
|
flags := os.O_CREATE | os.O_RDWR
|
|
|
|
if req.GetTruncate() {
|
|
|
|
flags = flags | os.O_TRUNC
|
|
|
|
}
|
|
|
|
file, err := os.OpenFile(path, flags, 0666)
|
2020-05-11 15:08:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return errors.InternalServerError("go.micro.server", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp.Id = h.session.Add(file)
|
|
|
|
rsp.Result = true
|
|
|
|
|
|
|
|
logger.Debugf("Open %s, sessionId=%d", req.Filename, rsp.Id)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) Close(ctx context.Context, req *proto.CloseRequest, rsp *proto.CloseResponse) error {
|
|
|
|
h.session.Delete(req.Id)
|
|
|
|
logger.Debugf("Close sessionId=%d", req.Id)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) Stat(ctx context.Context, req *proto.StatRequest, rsp *proto.StatResponse) error {
|
|
|
|
path := filepath.Join(h.readDir, req.Filename)
|
|
|
|
fi, err := os.Stat(path)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return errors.InternalServerError("go.micro.srv.file", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if fi.IsDir() {
|
|
|
|
rsp.Type = "Directory"
|
|
|
|
} else {
|
|
|
|
rsp.Type = "File"
|
|
|
|
rsp.Size = fi.Size()
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp.LastModified = fi.ModTime().Unix()
|
|
|
|
logger.Debugf("Stat %s, %#v", req.Filename, rsp)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) Read(ctx context.Context, req *proto.ReadRequest, rsp *proto.ReadResponse) error {
|
|
|
|
file := h.session.Get(req.Id)
|
|
|
|
if file == nil {
|
|
|
|
return errors.InternalServerError("go.micro.srv.file", "You must call open first.")
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp.Data = make([]byte, req.Size)
|
|
|
|
n, err := file.ReadAt(rsp.Data, req.Offset)
|
|
|
|
if err != nil && err != io.EOF {
|
|
|
|
return errors.InternalServerError("go.micro.srv.file", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if err == io.EOF {
|
|
|
|
rsp.Eof = true
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp.Size = int64(n)
|
|
|
|
rsp.Data = rsp.Data[:n]
|
|
|
|
|
|
|
|
logger.Debugf("Read sessionId=%d, Offset=%d, n=%d", req.Id, req.Offset, rsp.Size)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *handler) Write(ctx context.Context, req *proto.WriteRequest, rsp *proto.WriteResponse) error {
|
|
|
|
file := h.session.Get(req.Id)
|
|
|
|
if file == nil {
|
|
|
|
return errors.InternalServerError("go.micro.srv.file", "You must call open first.")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := file.WriteAt(req.GetData(), req.GetOffset()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Debugf("Write sessionId=%d, Offset=%d, n=%d", req.Id, req.Offset)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type session struct {
|
|
|
|
sync.Mutex
|
|
|
|
files map[int64]*os.File
|
|
|
|
counter int64
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *session) Add(file *os.File) int64 {
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
|
|
|
|
s.counter += 1
|
|
|
|
s.files[s.counter] = file
|
|
|
|
|
|
|
|
return s.counter
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *session) Get(id int64) *os.File {
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
return s.files[id]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *session) Delete(id int64) {
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
|
|
|
|
if file, exist := s.files[id]; exist {
|
|
|
|
file.Close()
|
|
|
|
delete(s.files, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *session) Len() int {
|
|
|
|
return len(s.files)
|
|
|
|
}
|