How CasaOS Handles File Upload Progress Tracking with the Progress Struct
CasaOS tracks file uploads by wrapping the data stream in a custom Progress struct that implements io.Writer, calculating percentage completion on every write operation and invoking a callback to report progress to the UI.
CasaOS (IceWhaleTech/CasaOS) provides a generic mechanism for real-time file upload progress tracking that works across local and cloud storage drivers. At the heart of this system lies the Progress struct defined in internal/driver/driver.go, which bridges the gap between raw byte streams and user-visible percentage indicators. This architecture allows any storage backend to report granular upload status without implementing driver-specific progress logic.
The Core Architecture of the Progress Struct
The UpdateProgress Callback Type
The foundation of CasaOS upload progress tracking is the UpdateProgress function type defined at approximately line 113 in internal/driver/driver.go. This callback accepts an integer representing the completion percentage.
type UpdateProgress func(percentage int)
Progress Struct Definition
The Progress struct encapsulates the total byte count, bytes written so far, and the callback function. Defined at lines 115-118 in internal/driver/driver.go, it maintains the state necessary to calculate completion percentages.
type Progress struct {
Total int64 // total size of the upload
Done int64 // bytes already written
up UpdateProgress // callback to report the current percent
}
How the Progress Writer Calculates Upload Percentage
The Progress struct implements the standard io.Writer interface through its Write method (lines 121-125). Every time a chunk of bytes is written, the method updates the Done counter, computes the current percentage using floating-point division, and invokes the callback.
func (p *Progress) Write(b []byte) (n int, err error) {
n = len(b)
p.Done += int64(n)
p.up(int(float64(p.Done) / float64(p.Total) * 100))
return
}
This implementation ensures that CasaOS file upload progress tracking remains accurate regardless of chunk size, as the percentage calculation occurs on every write operation.
Creating Progress Trackers with the Factory Function
To instantiate a new progress tracker, CasaOS provides the NewProgress factory function at lines 128-133 in internal/driver/driver.go. This helper initializes the struct with the total file size and the update callback.
func NewProgress(total int64, up UpdateProgress) *Progress {
return &Progress{Total: total, up: up}
}
Integration with Storage Drivers
Driver Interface Requirements
Every storage driver in CasaOS implements a Put method that accepts an UpdateProgress argument, defined at lines 31-33 in internal/driver/driver.go. This standardizes progress reporting across all backends including local storage, Google Drive, and OneDrive.
Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error
Google Drive Implementation Example
The Google Drive driver (drivers/google_drive/drive.go) demonstrates how drivers receive and utilize the progress callback. While the driver ultimately streams the file via stream.GetReadCloser() (lines 75-80), the caller can wrap the stream with a Progress writer before invoking Put, as shown in lines 31-33.
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
// ...
req.SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).SetBody(stream.GetReadCloser())
// ...
}
Practical Usage Pattern for Real-Time Progress
When an API endpoint needs to expose real-time upload progress to a web UI, it creates a Progress object with the total file size, provides a callback that pushes the percentage over a websocket or SSE, and then copies the incoming data through that writer. The io.Copy call triggers prog.Write, which updates the progress percentage and invokes the UI callback.
func uploadHandler(c echo.Context) error {
// 1. read multipart file header
fh, err := c.FormFile("file")
if err != nil { return err }
// 2. total size for progress tracking
total := fh.Size
// 3. create a Progress writer that forwards percent to the client
prog := driver.NewProgress(total, func(p int) {
// broadcast `p` to the front-end (e.g., via websocket)
log.Printf("upload %d%% complete", p)
})
// 4. open the uploaded file (multipart part)
src, err := fh.Open()
if err != nil { return err }
defer src.Close()
// 5. copy through Progress – this fires the callback on every chunk
if _, err = io.Copy(prog, src); err != nil {
return err
}
return nil
}
Chunked Upload Handling
CasaOS also provides a FileUploadService (see service/file_upload.go, lines 16-80) that manages chunked uploads by storing each chunk on disk and assembling the final file. While this service handles chunk bookkeeping, it can wrap the incoming data stream with a Progress writer before writing each chunk to provide visual progress feedback to the user.
Summary
- CasaOS file upload progress tracking relies on the
Progressstruct ininternal/driver/driver.goto wrap data streams and report completion percentages. - The
Progressstruct implementsio.Writerby updating aDonecounter and invoking anUpdateProgresscallback on every write operation (lines 121-125). - Storage drivers receive progress callbacks through the standard
Putmethod interface (lines 31-33), enabling consistent progress reporting across local and cloud backends. - The
NewProgressfactory function (lines 128-133) creates ready-to-use progress trackers that calculate percentages usingDone/Total*100. - Handlers can stream uploads through the
Progresswriter usingio.Copy, triggering real-time UI updates via websockets or SSE.
Frequently Asked Questions
What is the Progress struct in CasaOS?
The Progress struct is a custom writer defined in internal/driver/driver.go (lines 115-118) that tracks how many bytes have been written during a file upload. It stores the total file size, the current bytes written (Done), and a callback function (up) that receives the completion percentage.
How does CasaOS calculate upload percentage?
CasaOS calculates upload percentage in the Write method of the Progress struct (lines 121-125). After each write operation, it computes the percentage using the formula int(float64(p.Done) / float64(p.Total) * 100) and passes this integer to the UpdateProgress callback.
Can CasaOS track progress for cloud storage uploads?
Yes, CasaOS supports progress tracking for cloud storage drivers including Google Drive, OneDrive, and others. The driver interface requires a Put method that accepts an UpdateProgress callback (lines 31-33 in internal/driver/driver.go), allowing the same Progress struct to report status regardless of whether the destination is local disk or remote cloud storage.
How does the Progress struct integrate with chunked uploads?
While the FileUploadService (service/file_upload.go, lines 16-80) manages the logic for storing and assembling upload chunks on disk, the Progress struct provides the visual feedback mechanism. The service can wrap each chunk's data stream with a Progress writer before saving it, ensuring users see continuous progress updates even when files are uploaded in multiple pieces.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →