From e2c0f0b12c9f6b6c04789a1044ccd37e0e371361 Mon Sep 17 00:00:00 2001 From: Bagas Aulia Rezki Date: Mon, 16 Sep 2024 22:03:43 +0700 Subject: [PATCH] Optimize file chunk validation by reducing system calls --- handler/file/file.go | 15 +++--- handler/file/visibility/visibility.go | 12 ++--- handler/user/user.go | 67 ++++++++++++++++++++------- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/handler/file/file.go b/handler/file/file.go index 28d1b0e..cbb2e25 100644 --- a/handler/file/file.go +++ b/handler/file/file.go @@ -8,7 +8,6 @@ import ( "github.com/fossyy/filekeeper/utils" fileView "github.com/fossyy/filekeeper/view/client/file" "net/http" - "os" "path/filepath" "strconv" ) @@ -22,17 +21,15 @@ func GET(w http.ResponseWriter, r *http.Request) { return } var filesData []types.FileData + for _, file := range files { saveFolder := filepath.Join("uploads", userSession.UserID.String(), file.ID.String(), file.Name) - missingChunk := false - for j := 0; j < int(file.TotalChunk); j++ { - fileName := fmt.Sprintf("%s/chunk_%d", saveFolder, j) - if _, err := os.Stat(fileName); os.IsNotExist(err) { - missingChunk = true - break - } - } + pattern := fmt.Sprintf("%s/chunk_*", saveFolder) + chunkFiles, err := filepath.Glob(pattern) + + missingChunk := err != nil || len(chunkFiles) != int(file.TotalChunk) + filesData = append(filesData, types.FileData{ ID: file.ID.String(), Name: file.Name, diff --git a/handler/file/visibility/visibility.go b/handler/file/visibility/visibility.go index a7f4e8b..343c5ad 100644 --- a/handler/file/visibility/visibility.go +++ b/handler/file/visibility/visibility.go @@ -7,7 +7,6 @@ import ( "github.com/fossyy/filekeeper/utils" fileView "github.com/fossyy/filekeeper/view/client/file" "net/http" - "os" "path/filepath" "strconv" ) @@ -34,15 +33,10 @@ func PUT(w http.ResponseWriter, r *http.Request) { return } saveFolder := filepath.Join("uploads", userSession.UserID.String(), file.ID.String(), file.Name) - missingChunk := false - for j := 0; j < int(file.TotalChunk); j++ { - fileName := fmt.Sprintf("%s/chunk_%d", saveFolder, j) + pattern := fmt.Sprintf("%s/chunk_*", saveFolder) + chunkFiles, err := filepath.Glob(pattern) - if _, err := os.Stat(fileName); os.IsNotExist(err) { - missingChunk = true - break - } - } + missingChunk := err != nil || len(chunkFiles) != int(file.TotalChunk) fileData := types.FileData{ ID: file.ID.String(), Name: file.Name, diff --git a/handler/user/user.go b/handler/user/user.go index 9c3a127..020647d 100644 --- a/handler/user/user.go +++ b/handler/user/user.go @@ -15,7 +15,6 @@ import ( "github.com/gorilla/websocket" "gorm.io/gorm" "net/http" - "os" "path/filepath" "strings" ) @@ -199,15 +198,31 @@ func handlerWS(conn *websocket.Conn, userSession types.User) { Downloaded: newFile.Downloaded, } fileData.Chunk = make(map[string]bool) - fileData.Done = true + saveFolder := filepath.Join("uploads", userSession.UserID.String(), newFile.ID.String(), newFile.Name) - for i := 0; i <= int(newFile.TotalChunk); i++ { - fileName := fmt.Sprintf("%s/chunk_%d", saveFolder, i) - if _, err := os.Stat(fileName); os.IsNotExist(err) { + + pattern := fmt.Sprintf("%s/chunk_*", saveFolder) + chunkFiles, err := filepath.Glob(pattern) + if err != nil { + app.Server.Logger.Error(err.Error()) + fileData.Done = false + } else { + for i := 0; i <= int(newFile.TotalChunk); i++ { fileData.Chunk[fmt.Sprintf("chunk_%d", i)] = false - fileData.Done = false - } else { - fileData.Chunk[fmt.Sprintf("chunk_%d", i)] = true + } + + for _, chunkFile := range chunkFiles { + var chunkIndex int + fmt.Sscanf(filepath.Base(chunkFile), "chunk_%d", &chunkIndex) + + fileData.Chunk[fmt.Sprintf("chunk_%d", chunkIndex)] = true + } + + for i := 0; i <= int(newFile.TotalChunk); i++ { + if !fileData.Chunk[fmt.Sprintf("chunk_%d", i)] { + fileData.Done = false + break + } } } sendSuccessResponseWithID(conn, action.Action, fileData, uploadNewFile.RequestID) @@ -227,21 +242,37 @@ func handlerWS(conn *websocket.Conn, userSession types.User) { Name: file.Name, Size: file.Size, Downloaded: file.Downloaded, - Done: false, + Chunk: make(map[string]bool), + Done: true, } - fileData.Chunk = make(map[string]bool) - fileData.Done = true - saveFolder := filepath.Join("uploads", userSession.UserID.String(), fileData.ID.String(), fileData.Name) - for i := 0; i <= int(file.TotalChunk-1); i++ { - fileName := fmt.Sprintf("%s/chunk_%d", saveFolder, i) - if _, err := os.Stat(fileName); os.IsNotExist(err) { + saveFolder := filepath.Join("uploads", userSession.UserID.String(), fileData.ID.String(), fileData.Name) + pattern := fmt.Sprintf("%s/chunk_*", saveFolder) + chunkFiles, err := filepath.Glob(pattern) + + if err != nil { + app.Server.Logger.Error(err.Error()) + fileData.Done = false + } else { + for i := 0; i <= int(file.TotalChunk); i++ { fileData.Chunk[fmt.Sprintf("chunk_%d", i)] = false - fileData.Done = false - } else { - fileData.Chunk[fmt.Sprintf("chunk_%d", i)] = true + } + + for _, chunkFile := range chunkFiles { + var chunkIndex int + fmt.Sscanf(filepath.Base(chunkFile), "chunk_%d", &chunkIndex) + + fileData.Chunk[fmt.Sprintf("chunk_%d", chunkIndex)] = true + } + + for i := 0; i <= int(file.TotalChunk); i++ { + if !fileData.Chunk[fmt.Sprintf("chunk_%d", i)] { + fileData.Done = false + break + } } } + sendSuccessResponseWithID(conn, action.Action, fileData, uploadNewFile.RequestID) continue case Ping: