155 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package file
 | |
| 
 | |
| import (
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"sync"
 | |
| 
 | |
| 	"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"
 | |
| 	"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)
 | |
| 	flags := os.O_CREATE | os.O_RDWR
 | |
| 	if req.GetTruncate() {
 | |
| 		flags = flags | os.O_TRUNC
 | |
| 	}
 | |
| 	file, err := os.OpenFile(path, flags, 0666)
 | |
| 	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 session id=%d, Offset=%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)
 | |
| }
 |