Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
198: Working with ZIP Archives
A few years ago, I worked with a developer who was building a telemetry exporter for a high-traffic microservice. He was trying to upload thousands of small JSON log files to an S3 bucket every hour. At first, he just uploaded them individually, but he quickly hit two walls: the API request costs were skyrocketing, and the upload process was agonizingly slow due to the overhead of thousands of individual HTTP requests. He came to me frustrated because the network latency was killing his performance. The fix was simple—bundle those files into a ZIP archive locally before shipping them. It turned a ten-minute upload process into a ten-second one.
Bundling Files with zip.Writer
In Go, the archive/zip package is your primary tool here. To create an archive, you don't just "save" a file; you create a zip.Writer that wraps an existing io.Writer (usually an os.File). Think of the zip.Writer as a stream that formats your data into the ZIP specification as you feed it files.
The trickiest part for most people is the workflow of adding a file. You have to explicitly create a "header" for each file you want to add using writer.Create("filename.txt"). This method returns a new io.Writer. Anything you write to that returned writer gets compressed and tucked into the archive under that filename. I've seen plenty of bugs where developers forget to call Close() on the zip.Writer—if you do that, the ZIP's central directory won't be written, and the resulting file will be corrupted and unreadable.
package main
import (
"archive/zip"
"io"
"os"
)
func createArchive(outFile string, files []string) error {
// Create the physical file on disk
f, err := os.Create(outFile)
if err != nil {
return err
}
defer f.Close()
// Wrap the file in a zip writer
zw := zip.NewWriter(f)
defer zw.Close() // Crucial! This writes the ZIP central directory.
for _, file := range files {
// Create a entry in the zip
w, err := zw.Create(file)
if err != nil {
return err
}
// Open the actual file to be compressed
content, err := os.Open(file)
if err != nil {
return err
}
defer content.Close()
// Stream the file content into the zip entry
if _, err := io.Copy(w, content); err != nil {
return err
}
}
return nil
}
Unpacking and the Zip Slip Danger
Reading a ZIP file is a bit different. You use zip.OpenReader, which gives you a *zip.ReadCloser. You can then iterate through the files in the archive using a simple range loop. However, there is a serious security concern you need to be aware of called "Zip Slip."
If you blindly trust the filename inside a ZIP archive, a malicious actor could name a file ../../etc/passwd. If your code simply joins that filename to your destination directory, you might accidentally overwrite critical system files. I always recommend cleaning the destination path using filepath.Join and then checking if the resulting absolute path still resides within your intended target directory. Don't skip this check if you're handling files uploaded by users.
package main
import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
func unzip(src string, dest string) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer r.Close()
for _, f := range r.File {
// Prevent Zip Slip: Ensure the file stays within the dest folder
fpath := filepath.Join(dest, f.Name)
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", fpath)
}
if f.FileInfo().IsDir() {
os.MkdirAll(fpath, os.ModePerm)
continue
}
if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return err
}
rc, err := f.Open()
if err != nil {
return err
}
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
rc.Close()
return err
}
_, err = io.Copy(outFile, rc)
outFile.Close()
rc.Close()
if err != nil {
return err
}
}
return nil
}
📋 Practical Task
Build a Configuration Bundle Tool
You need to create a utility that helps your team package environment-specific configurations for deployment. Build a program that does the following:
- Scans a directory named
./configsfor any files ending in.yamlor.json. - Bundles all matching files into a single archive named
deployment_bundle.zip. - Includes a generated file inside the ZIP called
manifest.txtthat lists the names and sizes of all files included in the bundle. - Ensures that the
zip.Writeris properly closed to avoid archive corruption.
Test your tool by creating a few dummy YAML files in a ./configs folder and verifying that the resulting ZIP contains both your config files and the generated manifest.
There are no comments for now.