Add resumable upload feature with progress display
- Implement chunked file upload with 1MB chunk size - Add progress bar with percentage and chunk counter (e.g., 2/5) - Support resuming interrupted uploads - Improve UI with better progress visualization - Add dropzone.js integration for drag-and-drop uploads - Fix progress bar jumping issue in resumable uploads - Add file type icons and size display - Enhance error handling and user feedback
This commit is contained in:
129
main.go
129
main.go
@@ -391,6 +391,22 @@ func uploadFile(c *gin.Context) {
|
||||
path = "."
|
||||
}
|
||||
|
||||
dzuuid := c.PostForm("dzuuid")
|
||||
chunkIndex := c.PostForm("dzchunkindex")
|
||||
totalChunkCount := c.PostForm("dztotalchunkcount")
|
||||
fileName := c.PostForm("dzanonymousfilename")
|
||||
|
||||
isChunked := dzuuid != "" && chunkIndex != ""
|
||||
|
||||
tmpDir := filepath.Join(rootDir, ".tmp", "chunks")
|
||||
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
@@ -401,7 +417,12 @@ func uploadFile(c *gin.Context) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fullPath := filepath.Join(rootDir, path, header.Filename)
|
||||
targetFileName := header.Filename
|
||||
if fileName != "" {
|
||||
targetFileName = fileName
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(rootDir, path, targetFileName)
|
||||
if !strings.HasPrefix(fullPath, rootDir) {
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
@@ -410,24 +431,98 @@ func uploadFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
outFile, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
if isChunked {
|
||||
idx := 0
|
||||
fmt.Sscanf(chunkIndex, "%d", &idx)
|
||||
total := 0
|
||||
if totalChunkCount != "" {
|
||||
fmt.Sscanf(totalChunkCount, "%d", &total)
|
||||
}
|
||||
|
||||
chunkDir := filepath.Join(tmpDir, dzuuid)
|
||||
if err := os.MkdirAll(chunkDir, 0755); err != nil {
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
chunkPath := filepath.Join(chunkDir, fmt.Sprintf("chunk_%d", idx))
|
||||
outFile, err := os.Create(chunkPath)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
io.Copy(outFile, file)
|
||||
|
||||
if idx == total-1 && total > 0 {
|
||||
finalFile, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
os.RemoveAll(chunkDir)
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer finalFile.Close()
|
||||
|
||||
for i := 0; i < total; i++ {
|
||||
chunkPath := filepath.Join(chunkDir, fmt.Sprintf("chunk_%d", i))
|
||||
chunkFile, err := os.Open(chunkPath)
|
||||
if err != nil {
|
||||
finalFile.Close()
|
||||
os.RemoveAll(chunkDir)
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
io.Copy(finalFile, chunkFile)
|
||||
chunkFile.Close()
|
||||
os.Remove(chunkPath)
|
||||
}
|
||||
os.RemoveAll(chunkDir)
|
||||
|
||||
watchChan <- WatchEvent{Type: "create", Path: fullPath, Name: targetFileName}
|
||||
|
||||
c.JSON(http.StatusOK, UploadResponse{
|
||||
Success: true,
|
||||
Message: "上传成功",
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, UploadResponse{
|
||||
Success: true,
|
||||
Message: "分块上传成功",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
outFile, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, UploadResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
io.Copy(outFile, file)
|
||||
|
||||
watchChan <- WatchEvent{Type: "create", Path: fullPath, Name: targetFileName}
|
||||
|
||||
c.JSON(http.StatusOK, UploadResponse{
|
||||
Success: true,
|
||||
Message: "上传成功",
|
||||
})
|
||||
return
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
io.Copy(outFile, file)
|
||||
|
||||
watchChan <- WatchEvent{Type: "create", Path: fullPath, Name: header.Filename}
|
||||
|
||||
c.JSON(http.StatusOK, UploadResponse{
|
||||
Success: true,
|
||||
Message: "上传成功",
|
||||
})
|
||||
}
|
||||
|
||||
func deleteFiles(c *gin.Context) {
|
||||
|
||||
Reference in New Issue
Block a user