mirror of
https://github.com/golang/go
synced 2024-11-17 18:14:46 -07:00
archive/zip: enable overriding (de)compressors per file
Implement setting the compression level for a zip archive by registering a per-Writer compressor through Writer.RegisterCompressor. If no compressors are registered, fall back to the ones registered at the package level. Also implements per-Reader decompressors. Fixes #8359 Change-Id: I93b27c81947b0f817b42e0067aa610ff267fdb21 Reviewed-on: https://go-review.googlesource.com/16669 Reviewed-by: Joe Tsai <joetsai@digital-static.net> Run-TryBot: Joe Tsai <joetsai@digital-static.net> Reviewed-by: Klaus Post <klauspost@gmail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
a4cd0a49c7
commit
46300a058d
@ -7,6 +7,7 @@ package zip_test
|
|||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/flate"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@ -73,3 +74,31 @@ func ExampleReader() {
|
|||||||
// Contents of README:
|
// Contents of README:
|
||||||
// This is the source code repository for the Go programming language.
|
// This is the source code repository for the Go programming language.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleWriter_RegisterCompressor() {
|
||||||
|
// Override the default Deflate compressor with a higher compression
|
||||||
|
// level.
|
||||||
|
|
||||||
|
// Create a buffer to write our archive to.
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
// Create a new zip archive.
|
||||||
|
w := zip.NewWriter(buf)
|
||||||
|
|
||||||
|
var fw *flate.Writer
|
||||||
|
|
||||||
|
// Register the deflator.
|
||||||
|
w.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
|
||||||
|
var err error
|
||||||
|
if fw == nil {
|
||||||
|
// Creating a flate compressor for every file is
|
||||||
|
// expensive, create one and reuse it.
|
||||||
|
fw, err = flate.NewWriter(out, flate.BestCompression)
|
||||||
|
} else {
|
||||||
|
fw.Reset(out)
|
||||||
|
}
|
||||||
|
return fw, err
|
||||||
|
})
|
||||||
|
|
||||||
|
// Proceed to add files to w.
|
||||||
|
}
|
||||||
|
@ -22,9 +22,10 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
r io.ReaderAt
|
r io.ReaderAt
|
||||||
File []*File
|
File []*File
|
||||||
Comment string
|
Comment string
|
||||||
|
decompressors map[uint16]Decompressor
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReadCloser struct {
|
type ReadCloser struct {
|
||||||
@ -34,6 +35,7 @@ type ReadCloser struct {
|
|||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
FileHeader
|
FileHeader
|
||||||
|
zip *Reader
|
||||||
zipr io.ReaderAt
|
zipr io.ReaderAt
|
||||||
zipsize int64
|
zipsize int64
|
||||||
headerOffset int64
|
headerOffset int64
|
||||||
@ -95,7 +97,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
|
|||||||
// a bad one, and then only report a ErrFormat or UnexpectedEOF if
|
// a bad one, and then only report a ErrFormat or UnexpectedEOF if
|
||||||
// the file count modulo 65536 is incorrect.
|
// the file count modulo 65536 is incorrect.
|
||||||
for {
|
for {
|
||||||
f := &File{zipr: r, zipsize: size}
|
f := &File{zip: z, zipr: r, zipsize: size}
|
||||||
err = readDirectoryHeader(f, buf)
|
err = readDirectoryHeader(f, buf)
|
||||||
if err == ErrFormat || err == io.ErrUnexpectedEOF {
|
if err == ErrFormat || err == io.ErrUnexpectedEOF {
|
||||||
break
|
break
|
||||||
@ -113,6 +115,26 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterDecompressor registers or overrides a custom decompressor for a
|
||||||
|
// specific method ID. If a decompressor for a given method is not found,
|
||||||
|
// Reader will default to looking up the decompressor at the package level.
|
||||||
|
//
|
||||||
|
// Must not be called concurrently with Open on any Files in the Reader.
|
||||||
|
func (z *Reader) RegisterDecompressor(method uint16, dcomp Decompressor) {
|
||||||
|
if z.decompressors == nil {
|
||||||
|
z.decompressors = make(map[uint16]Decompressor)
|
||||||
|
}
|
||||||
|
z.decompressors[method] = dcomp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Reader) decompressor(method uint16) Decompressor {
|
||||||
|
dcomp := z.decompressors[method]
|
||||||
|
if dcomp == nil {
|
||||||
|
dcomp = decompressor(method)
|
||||||
|
}
|
||||||
|
return dcomp
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes the Zip file, rendering it unusable for I/O.
|
// Close closes the Zip file, rendering it unusable for I/O.
|
||||||
func (rc *ReadCloser) Close() error {
|
func (rc *ReadCloser) Close() error {
|
||||||
return rc.f.Close()
|
return rc.f.Close()
|
||||||
@ -140,7 +162,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
|
|||||||
}
|
}
|
||||||
size := int64(f.CompressedSize64)
|
size := int64(f.CompressedSize64)
|
||||||
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
|
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
|
||||||
dcomp := decompressor(f.Method)
|
dcomp := f.zip.decompressor(f.Method)
|
||||||
if dcomp == nil {
|
if dcomp == nil {
|
||||||
err = ErrAlgorithm
|
err = ErrAlgorithm
|
||||||
return
|
return
|
||||||
|
@ -14,14 +14,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TODO(adg): support zip file comments
|
// TODO(adg): support zip file comments
|
||||||
// TODO(adg): support specifying deflate level
|
|
||||||
|
|
||||||
// Writer implements a zip file writer.
|
// Writer implements a zip file writer.
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
cw *countWriter
|
cw *countWriter
|
||||||
dir []*header
|
dir []*header
|
||||||
last *fileWriter
|
last *fileWriter
|
||||||
closed bool
|
closed bool
|
||||||
|
compressors map[uint16]Compressor
|
||||||
}
|
}
|
||||||
|
|
||||||
type header struct {
|
type header struct {
|
||||||
@ -220,7 +220,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
|
|||||||
compCount: &countWriter{w: w.cw},
|
compCount: &countWriter{w: w.cw},
|
||||||
crc32: crc32.NewIEEE(),
|
crc32: crc32.NewIEEE(),
|
||||||
}
|
}
|
||||||
comp := compressor(fh.Method)
|
comp := w.compressor(fh.Method)
|
||||||
if comp == nil {
|
if comp == nil {
|
||||||
return nil, ErrAlgorithm
|
return nil, ErrAlgorithm
|
||||||
}
|
}
|
||||||
@ -270,6 +270,24 @@ func writeHeader(w io.Writer, h *FileHeader) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterCompressor registers or overrides a custom compressor for a specific
|
||||||
|
// method ID. If a compressor for a given method is not found, Writer will
|
||||||
|
// default to looking up the compressor at the package level.
|
||||||
|
func (w *Writer) RegisterCompressor(method uint16, comp Compressor) {
|
||||||
|
if w.compressors == nil {
|
||||||
|
w.compressors = make(map[uint16]Compressor)
|
||||||
|
}
|
||||||
|
w.compressors[method] = comp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) compressor(method uint16) Compressor {
|
||||||
|
comp := w.compressors[method]
|
||||||
|
if comp == nil {
|
||||||
|
comp = compressor(method)
|
||||||
|
}
|
||||||
|
return comp
|
||||||
|
}
|
||||||
|
|
||||||
type fileWriter struct {
|
type fileWriter struct {
|
||||||
*header
|
*header
|
||||||
zipw io.Writer
|
zipw io.Writer
|
||||||
|
Loading…
Reference in New Issue
Block a user