Chunk File Uploading on GoLang

Prajwol KC
2 min readMar 5, 2022

This is the backend implementation of chunk file upload under golang platform. The backend system is only implemented. The philosophy behind is it attempted to upload everything in a single request. So, with the chunking strategy, they processed fixed-size chunk requests until all of the file parts are uploaded.

File Chunk data are transmitted from the client-side with the headers like content-type, content-range, content expiry, etc, and file form data (filename, filetype). Therefore each chunk is processed by the server to get uploaded on the desired condition.

Go lang main.go code

ChunkUploadHandler.go

package controllerimport ("io""strconv""strings""net/http""os""github.com/gin-gonic/gin")
// Input -> add binding model for input
type Input struct {Model string `form:"model,omitempty" binding:"required"`}// Response -> response for the util scopetype Response struct {Success bool `json:"success"`Message string `json:"message"`Name string `json:"name"`}func ChunkUploadHandler(c *gin.Context) {var f *os.Filefile, uploadFile, err := c.Request.FormFile("file")if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "content-type should be multipart/formdata"})return}contentRangeHeader := c.Request.Header.Get("Content-Range")rangeAndSize := strings.Split(contentRangeHeader, "/")rangeParts := strings.Split(rangeAndSize[0], "-")rangeMax, err := strconv.Atoi(rangeParts[1])if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Missing range in Content-Range header"})return}fileSize, err := strconv.Atoi(rangeAndSize[1])if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Missing file size in Content-Range header"})return}if fileSize > 100*1024*1024 {c.JSON(http.StatusBadRequest, gin.H{"error": "File size should be less than 100MB"})return}var input Inputerr = c.ShouldBind(&input)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Missing model name in header"})return}tempDir, err := os.UserHomeDir()if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Error resolving home directory"})return}if _, err := os.Stat(tempDir + "/" + input.Model); os.IsNotExist(err) {err := os.Mkdir(tempDir+"/"+input.Model, 0777)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Error creating temporary directory"})return}}if f == nil {f, err = os.OpenFile(tempDir+"/"+input.Model+"/"+uploadFile.Filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Error creating file"})return}}if _, err := io.Copy(f, file); err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Error writing to a file"})return}f.Close()if rangeMax >= fileSize-1 {combinedFile := tempDir + "/" + input.Model + "/" + uploadFile.FilenameuploadingFile, err := os.Open(combinedFile)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to upload file "})return}uploadingFile.Close()response := &Response{Success: true,Message: "Uploaded Successfully",Name: uploadFile.Filename,}c.JSON(http.StatusOK, gin.H{"data": response})return}c.JSON(http.StatusOK, gin.H{"status": "uploading"})}

Originally published at https://prajwol-kc.com.np on March 5, 2022.

--

--