move request/response to internal api
This commit is contained in:
parent
3e48bd14df
commit
5dbd59fe8f
@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ResponseError struct {
|
||||
Err string `json:"error"`
|
||||
Details []string `json:"details"`
|
||||
}
|
||||
|
||||
func (u *ResponseError) Error() string {
|
||||
if len(u.Details) == 0 {
|
||||
return u.Err
|
||||
}
|
||||
return fmt.Sprintf("%s: \n - %s", u.Err, strings.Join(u.Details, "\n - "))
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
photosapi "gitlab.celogeek.com/photos/api/internal/photos/api"
|
||||
)
|
||||
|
||||
type LoginCommand struct {
|
||||
@ -12,15 +13,6 @@ type LoginCommand struct {
|
||||
Password string `short:"p" long:"password" description:"Password" required:"true"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Login string `json:"login"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
func (c *LoginCommand) Execute(args []string) error {
|
||||
logger.Printf("Login on %s...\n", c.Url)
|
||||
|
||||
@ -28,9 +20,12 @@ func (c *LoginCommand) Execute(args []string) error {
|
||||
|
||||
resp, err := cli.
|
||||
R().
|
||||
SetBody(&LoginRequest{c.Login, c.Password}).
|
||||
SetResult(&LoginResponse{}).
|
||||
SetError(&ResponseError{}).
|
||||
SetBody(&photosapi.LoginRequest{
|
||||
Login: c.Login,
|
||||
Password: c.Password,
|
||||
}).
|
||||
SetResult(&photosapi.LoginResponse{}).
|
||||
SetError(&photosapi.ErrorWithDetails{}).
|
||||
Post("/account/login")
|
||||
|
||||
if err != nil {
|
||||
@ -39,11 +34,11 @@ func (c *LoginCommand) Execute(args []string) error {
|
||||
|
||||
if resp.IsError() {
|
||||
logger.Printf("Login failed!")
|
||||
return resp.Error().(*ResponseError)
|
||||
return resp.Error().(*photosapi.ErrorWithDetails)
|
||||
}
|
||||
|
||||
logger.Println("Login succeed!")
|
||||
if result, ok := resp.Result().(*LoginResponse); ok {
|
||||
if result, ok := resp.Result().(*photosapi.LoginResponse); ok {
|
||||
fmt.Println(result.Token)
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
photosapi "gitlab.celogeek.com/photos/api/internal/photos/api"
|
||||
)
|
||||
|
||||
type RegisterCommand struct {
|
||||
@ -10,11 +11,6 @@ type RegisterCommand struct {
|
||||
Password string `short:"p" long:"password" description:"Password" required:"true"`
|
||||
}
|
||||
|
||||
type RegisterRequest struct {
|
||||
Login string `json:"login"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (c *RegisterCommand) Execute(args []string) error {
|
||||
logger.Printf("Registering on %s...\n", c.Url)
|
||||
|
||||
@ -22,8 +18,11 @@ func (c *RegisterCommand) Execute(args []string) error {
|
||||
|
||||
resp, err := cli.
|
||||
R().
|
||||
SetError(&ResponseError{}).
|
||||
SetBody(&RegisterRequest{c.Login, c.Password}).
|
||||
SetError(&photosapi.ErrorWithDetails{}).
|
||||
SetBody(&photosapi.SignupRequest{
|
||||
Login: c.Login,
|
||||
Password: c.Password,
|
||||
}).
|
||||
Post("/account/signup")
|
||||
|
||||
if err != nil {
|
||||
@ -32,7 +31,7 @@ func (c *RegisterCommand) Execute(args []string) error {
|
||||
|
||||
if resp.IsError() {
|
||||
logger.Println("Registering failed!")
|
||||
return resp.Error().(*ResponseError)
|
||||
return resp.Error().(*photosapi.ErrorWithDetails)
|
||||
}
|
||||
|
||||
logger.Println("Registering succeed!")
|
||||
|
@ -20,33 +20,22 @@ type UploadCommand struct {
|
||||
Workers uint32 `short:"w" long:"workers" description:"Number of workers for uploading chunks" default:"4"`
|
||||
}
|
||||
|
||||
type UploadCreate struct {
|
||||
UploadId string `json:"upload_id"`
|
||||
}
|
||||
|
||||
type UploadPartResult struct {
|
||||
UploadId string `json:"upload_id"`
|
||||
Part uint `json:"part"`
|
||||
Size uint `json:"size"`
|
||||
PartSha256 string `json:"sha256"`
|
||||
}
|
||||
|
||||
func (c *UploadCommand) Cli() *resty.Client {
|
||||
return resty.New().SetBaseURL(c.Url).SetAuthScheme("Private").SetAuthToken(c.Token)
|
||||
}
|
||||
|
||||
func (c *UploadCommand) Execute(args []string) error {
|
||||
cli := c.Cli()
|
||||
resp, err := cli.R().SetError(&ResponseError{}).SetResult(&UploadCreate{}).Post("/upload")
|
||||
resp, err := cli.R().SetError(&photosapi.ErrorWithDetails{}).SetResult(&photosapi.Upload{}).Post("/upload")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.IsError() {
|
||||
return resp.Error().(*ResponseError)
|
||||
return resp.Error().(*photosapi.ErrorWithDetails)
|
||||
}
|
||||
|
||||
uploadId := resp.Result().(*UploadCreate).UploadId
|
||||
uploadId := resp.Result().(*photosapi.Upload).Id
|
||||
|
||||
f, err := os.Open(c.File)
|
||||
if err != nil {
|
||||
@ -81,8 +70,7 @@ func (c *UploadCommand) Execute(args []string) error {
|
||||
|
||||
resp, err := cli.
|
||||
R().
|
||||
SetError(&ResponseError{}).
|
||||
SetResult(&UploadPartResult{}).
|
||||
SetError(&photosapi.ErrorWithDetails{}).
|
||||
SetQueryParam("part", fmt.Sprint(parts)).
|
||||
SetQueryParam("sha256", hex.EncodeToString(partsha256.Sum(nil))).
|
||||
SetBody(b[:n]).
|
||||
@ -94,19 +82,19 @@ func (c *UploadCommand) Execute(args []string) error {
|
||||
}
|
||||
|
||||
if resp.IsError() {
|
||||
return resp.Error().(*ResponseError)
|
||||
return resp.Error().(*photosapi.ErrorWithDetails)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
"Upload: %s\nParts: %d\n",
|
||||
"Result:\n - Upload ID: %s\n - Parts: %d\n",
|
||||
uploadId,
|
||||
parts,
|
||||
)
|
||||
|
||||
resp, err = cli.
|
||||
R().
|
||||
SetError(&ResponseError{}).
|
||||
SetError(&photosapi.ErrorWithDetails{}).
|
||||
SetPathParam("id", uploadId).
|
||||
SetBody(&photosapi.UploadCompleteRequest{
|
||||
Sha256: hex.EncodeToString(completesha256.Sum(nil)),
|
||||
@ -120,11 +108,9 @@ func (c *UploadCommand) Execute(args []string) error {
|
||||
}
|
||||
|
||||
if resp.IsError() {
|
||||
return resp.Error().(*ResponseError)
|
||||
return resp.Error().(*photosapi.ErrorWithDetails)
|
||||
}
|
||||
|
||||
fmt.Printf("Response: %s\n", resp.Body())
|
||||
|
||||
cli.R().SetPathParam("id", uploadId).Delete("/upload/{id}")
|
||||
|
||||
return nil
|
||||
|
@ -50,13 +50,22 @@ func NewAccount(login string, password string) *Account {
|
||||
}
|
||||
|
||||
// Service
|
||||
type SignupOrLoginRequest struct {
|
||||
Login string `binding:"min=3,max=40,alphanum"`
|
||||
Password string `binding:"min=8,max=40"`
|
||||
type SignupRequest struct {
|
||||
Login string `json:"login" binding:"required,min=3,max=40,alphanum"`
|
||||
Password string `json:"password" binding:"required,min=8,max=40"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func (s *Service) Signup(c *gin.Context) {
|
||||
var account *SignupOrLoginRequest
|
||||
var account *SignupRequest
|
||||
|
||||
if c.BindJSON(&account) != nil {
|
||||
return
|
||||
@ -80,7 +89,7 @@ func (s *Service) Signup(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (s *Service) Login(c *gin.Context) {
|
||||
var account *SignupOrLoginRequest
|
||||
var account *LoginRequest
|
||||
|
||||
if c.BindJSON(&account) != nil {
|
||||
return
|
||||
@ -96,9 +105,7 @@ func (s *Service) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"token": session.Token,
|
||||
})
|
||||
c.JSON(http.StatusOK, LoginResponse{session.Token})
|
||||
}
|
||||
|
||||
func (s *Service) Logout(c *gin.Context) {
|
||||
|
@ -2,6 +2,7 @@ package photosapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -11,6 +12,18 @@ var (
|
||||
ErrReqMissingBody = errors.New("missing body")
|
||||
)
|
||||
|
||||
type ErrorWithDetails struct {
|
||||
Err string `json:"error"`
|
||||
Details []string `json:"details"`
|
||||
}
|
||||
|
||||
func (u *ErrorWithDetails) Error() string {
|
||||
if len(u.Details) == 0 {
|
||||
return u.Err
|
||||
}
|
||||
return fmt.Sprintf("%s: \n - %s", u.Err, strings.Join(u.Details, "\n - "))
|
||||
}
|
||||
|
||||
func (s *Service) HandleError(c *gin.Context) {
|
||||
c.Next()
|
||||
err := c.Errors.Last()
|
||||
@ -18,20 +31,15 @@ func (s *Service) HandleError(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
details := err.Error()
|
||||
if details == "EOF" {
|
||||
details = "missing body"
|
||||
errWithDetails := &ErrorWithDetails{err.Error(), nil}
|
||||
|
||||
if errWithDetails.Err == "EOF" {
|
||||
errWithDetails.Err = "missing body"
|
||||
}
|
||||
|
||||
switch err.Type {
|
||||
case gin.ErrorTypeBind:
|
||||
c.JSON(-1, gin.H{
|
||||
"error": "binding error",
|
||||
"details": strings.Split(details, "\n"),
|
||||
})
|
||||
default:
|
||||
c.JSON(-1, gin.H{
|
||||
"error": details,
|
||||
})
|
||||
if err.Type == gin.ErrorTypeBind {
|
||||
errWithDetails.Err, errWithDetails.Details = "binding error", strings.Split(errWithDetails.Err, "\n")
|
||||
}
|
||||
|
||||
c.JSON(-1, errWithDetails)
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
photosstore "gitlab.celogeek.com/photos/api/internal/photos/store"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -23,7 +22,6 @@ type Service struct {
|
||||
Gin *gin.Engine
|
||||
DB *gorm.DB
|
||||
Config *ServiceConfig
|
||||
Store *photosstore.Store
|
||||
StorageTmp *Storage
|
||||
StorageUpload *Storage
|
||||
LogOk *Logger
|
||||
@ -40,7 +38,6 @@ func New(config *ServiceConfig) *Service {
|
||||
return &Service{
|
||||
Gin: gin.New(),
|
||||
Config: config,
|
||||
Store: &photosstore.Store{Path: config.StorePath},
|
||||
StorageTmp: NewStorage(config.StorePath, "tmp"),
|
||||
StorageUpload: NewStorage(config.StorePath, "upload"),
|
||||
LogOk: &Logger{os.Stdout, "Photos"},
|
||||
@ -67,7 +64,7 @@ func (s *Service) SetupRoutes() {
|
||||
}
|
||||
|
||||
func (s *Service) PrepareStore() {
|
||||
d, err := os.Stat(s.Store.Path)
|
||||
d, err := os.Stat(s.Config.StorePath)
|
||||
if err != nil {
|
||||
s.LogErr.Fatal("Store", err)
|
||||
}
|
||||
|
@ -6,10 +6,12 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Me struct {
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
func (s *Service) Me(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user": s.CurrentSession(c).Account.Login,
|
||||
})
|
||||
c.JSON(http.StatusOK, Me{s.CurrentSession(c).Account.Login})
|
||||
}
|
||||
|
||||
func (s *Service) MeInit() {
|
||||
|
@ -2,6 +2,7 @@ package photosapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -17,9 +18,9 @@ func (s *Service) Recovery(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
s.LogErr.Print("PANIC", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
|
||||
"error": ErrUnexpected.Error(),
|
||||
"details": err,
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, &ErrorWithDetails{
|
||||
ErrUnexpected.Error(),
|
||||
[]string{fmt.Sprint(err)},
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
@ -43,18 +43,18 @@ func (s *Service) UploadCreate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.StorageTmp.Create(sha.String()); err != nil {
|
||||
upload := &Upload{sha.String()}
|
||||
|
||||
if err := s.StorageTmp.Create(upload.Id); err != nil {
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"upload_id": sha.String(),
|
||||
})
|
||||
c.JSON(http.StatusCreated, upload)
|
||||
}
|
||||
|
||||
type UploadUri struct {
|
||||
Id string `uri:"upload_id" binding:"required,uuid"`
|
||||
type Upload struct {
|
||||
Id string `json:"upload_id" uri:"upload_id" binding:"required,uuid"`
|
||||
}
|
||||
|
||||
type UploadPartQuery struct {
|
||||
@ -64,7 +64,7 @@ type UploadPartQuery struct {
|
||||
|
||||
func (s *Service) UploadPart(c *gin.Context) {
|
||||
var (
|
||||
upload UploadUri
|
||||
upload Upload
|
||||
uploadPart UploadPartQuery
|
||||
)
|
||||
|
||||
@ -93,7 +93,7 @@ func (s *Service) UploadPart(c *gin.Context) {
|
||||
|
||||
sha := sha256.New()
|
||||
t := io.TeeReader(c.Request.Body, sha)
|
||||
w, err := io.Copy(f, t)
|
||||
_, err = io.Copy(f, t)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
os.Remove(tmp_file)
|
||||
@ -109,22 +109,18 @@ func (s *Service) UploadPart(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.Rename(tmp_file, file); err != nil {
|
||||
err = os.Rename(tmp_file, file)
|
||||
if err != nil {
|
||||
os.Remove(tmp_file)
|
||||
c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"upload_id": upload.Id,
|
||||
"part": uploadPart.Part,
|
||||
"size": w,
|
||||
"sha256": uploadPart.PartSha256,
|
||||
})
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Service) UploadCancel(c *gin.Context) {
|
||||
var upload UploadUri
|
||||
var upload Upload
|
||||
if c.BindUri(&upload) != nil {
|
||||
return
|
||||
}
|
||||
@ -145,7 +141,7 @@ type UploadCompleteRequest struct {
|
||||
|
||||
func (s *Service) UploadComplete(c *gin.Context) {
|
||||
var (
|
||||
upload UploadUri
|
||||
upload Upload
|
||||
uploadCompleteRequest UploadCompleteRequest
|
||||
)
|
||||
if c.BindUri(&upload) != nil || c.BindJSON(&uploadCompleteRequest) != nil {
|
||||
@ -157,11 +153,7 @@ func (s *Service) UploadComplete(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"sha256": uploadCompleteRequest.Sha256,
|
||||
"parts": uploadCompleteRequest.Parts,
|
||||
"name": uploadCompleteRequest.Name,
|
||||
})
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Service) UploadInit() {
|
||||
|
@ -1,122 +0,0 @@
|
||||
package photosstore
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStoreChunkAlreadyExists = errors.New("chunk file already exists")
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
type Chunk struct {
|
||||
*Store
|
||||
Sum string
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
func (s *Store) NewChunk(b []byte) *Chunk {
|
||||
sum := sha1.New()
|
||||
sum.Write(b)
|
||||
sumString := hex.EncodeToString(sum.Sum(nil))
|
||||
|
||||
return &Chunk{s, sumString, b}
|
||||
}
|
||||
|
||||
func (s *Store) LoadChunk(sum string) (*Chunk, error) {
|
||||
c := s.Chunk(sum)
|
||||
if !c.FileExists() {
|
||||
return nil, fmt.Errorf("chunk %s doesn't exists", sum)
|
||||
}
|
||||
b, err := os.ReadFile(c.Filename())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Bytes = b
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (s *Store) Chunk(sum string) *Chunk {
|
||||
return &Chunk{s, sum, nil}
|
||||
}
|
||||
|
||||
func (c *Chunk) Dir() string {
|
||||
return filepath.Join(c.Path, "storage", c.Sum[0:1], c.Sum[1:2])
|
||||
}
|
||||
|
||||
func (c *Chunk) Filename() string {
|
||||
return filepath.Join(c.Dir(), c.Sum)
|
||||
}
|
||||
|
||||
func (c *Chunk) Size() int64 {
|
||||
st, err := os.Stat(c.Filename())
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return st.Size()
|
||||
}
|
||||
|
||||
func (c *Chunk) FileExists() bool {
|
||||
fs, err := os.Stat(c.Filename())
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false
|
||||
}
|
||||
return !fs.IsDir()
|
||||
}
|
||||
|
||||
func (c *Chunk) Mkdir() error {
|
||||
return os.MkdirAll(c.Dir(), 0755)
|
||||
}
|
||||
|
||||
func (c *Chunk) Save(login string) error {
|
||||
if c.FileExists() {
|
||||
return ErrStoreChunkAlreadyExists
|
||||
}
|
||||
|
||||
if err := c.Mkdir(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(c.Filename(), c.Bytes, 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
meta, err := os.Create(c.Filename() + ".json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enc := json.NewEncoder(meta)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(gin.H{
|
||||
"author": login,
|
||||
"date": time.Now().UTC().Format("2006-01-02 15:04:05"),
|
||||
"checksum": c.Sum,
|
||||
"size": len(c.Bytes),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) Combine(sumb []string) (string, uint64, error) {
|
||||
sum := sha1.New()
|
||||
size := uint64(0)
|
||||
for _, sb := range sumb {
|
||||
c, err := s.LoadChunk(sb)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
sum.Write(c.Bytes)
|
||||
size += uint64(len(c.Bytes))
|
||||
}
|
||||
return hex.EncodeToString(sum.Sum(nil)), size, nil
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package photosstore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStoreMissingChunks = errors.New("part checksum missing")
|
||||
)
|
||||
|
||||
type StoreReaderChunk struct {
|
||||
Filename string
|
||||
Size int64
|
||||
}
|
||||
|
||||
type StoreReader struct {
|
||||
current *os.File
|
||||
chunk int
|
||||
chunks []StoreReaderChunk
|
||||
Size int64
|
||||
}
|
||||
|
||||
func (s *Store) NewStoreReader(chunks []string) (*StoreReader, error) {
|
||||
sr := &StoreReader{nil, 0, make([]StoreReaderChunk, len(chunks)), 0}
|
||||
for i, chunk := range chunks {
|
||||
c := s.Chunk(chunk)
|
||||
name := c.Filename()
|
||||
size := c.Size()
|
||||
if size < 0 {
|
||||
return nil, ErrStoreMissingChunks
|
||||
}
|
||||
sr.chunks[i] = StoreReaderChunk{name, size}
|
||||
sr.Size += size
|
||||
}
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (s *StoreReader) Read(p []byte) (n int, err error) {
|
||||
if s.current == nil {
|
||||
f, err := os.Open(s.chunks[s.chunk].Filename)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
s.current = f
|
||||
}
|
||||
|
||||
n, err = s.current.Read(p)
|
||||
if err == io.EOF {
|
||||
s.chunk++
|
||||
if s.chunk > len(s.chunks)-1 {
|
||||
return
|
||||
}
|
||||
s.Close()
|
||||
return s.Read(p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *StoreReader) Close() {
|
||||
if s.current != nil {
|
||||
s.current.Close()
|
||||
s.current = nil
|
||||
}
|
||||
}
|
||||
|
||||
// func (s *StoreReader) Seek(offset int64, whence int) (int64, error) {
|
||||
|
||||
// }
|
Loading…
x
Reference in New Issue
Block a user