Go version
go version go1.24.4 darwin/arm64
Output of go env
in your module/workspace:
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/carlos/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/carlos/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/qj/36_tzh_54kj9mm0lhlhy3f900000gn/T/go-build3621002674=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/carlos/Developer/goreleaser/goreleaser/go.mod'
GOMODCACHE='/Users/carlos/Developer/Go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/carlos/Developer/Go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.24.4/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/carlos/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.24.4/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.4'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
Creating a zip archive using archive/zip
like so:
package main
import (
"archive/zip"
"compress/flate"
"io"
"os"
)
func main() {
fz, err := os.OpenFile("foo.zip", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o644)
if err != nil {
panic(err)
}
z := zip.NewWriter(fz)
z.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
return flate.NewWriter(out, flate.BestCompression)
})
f, err := os.Open("main.go")
if err != nil {
panic(err)
}
fd, err := f.Stat()
if err != nil {
panic(err)
}
fh, err := zip.FileInfoHeader(fd)
if err != nil {
panic(err)
}
w, err := z.CreateHeader(fh)
if err != nil {
panic(err)
}
_, err = io.Copy(w, f)
if err != nil {
panic(err)
}
if err := z.Close(); err != nil {
panic(err)
}
}
Then, you run it, it should create a foo.zip
file.
From there, we can do things like:
$ busybox unzip -l foo.zip
Archive: foo.zip
Length Date Time Name
--------- ---------- ----- ----
715 06-13-2025 13:17 main.go
-------- -------
715 1 files
But, if you try to unzip it from a pipe:
$ cat foo.zip | busybox unzip -
Archive: -
unzip: zip flag 8 (streaming) is not supported
If we do the same with unzip
shipped by Apple on macOS, it only prints its own help.
My guess is that this line might be the culprit: https://cs.opensource.google/go/go/+/master:src/archive/zip/writer.go;l=359
It seems we're always setting the stream bit if the file isn't a directory...
What did you see happen?
unzip: zip flag 8 (streaming) is not supported
What did you expect to see?
Not to see that error.
Comment From: adonovan
Unlike tar ("tape archive") files of old, the design of zip files is inherently random-access due to the placement of the central directory at the end. While it is technically possible to create zip files from a stream of unknown files, I don't think that was ever considered in scope for archive/zip, and it's not possible to read any zip file without random access.
Comment From: cherrymui
cc @dsnet @bradfitz