1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
17 // TODO(adg): support zip file comments
18 // TODO(adg): support specifying deflate level
20 // Writer implements a zip file writer.
33 // NewWriter returns a new Writer writing a zip file to w.
34 func NewWriter(w io.Writer) *Writer {
35 return &Writer{countWriter: &countWriter{w: bufio.NewWriter(w)}}
38 // Close finishes writing the zip file by writing the central directory.
39 // It does not (and can not) close the underlying writer.
40 func (w *Writer) Close() (err os.Error) {
41 if w.last != nil && !w.last.closed {
42 if err = w.last.close(); err != nil {
48 return os.NewError("zip: writer closed twice")
52 defer recoverError(&err)
54 // write central directory
56 for _, h := range w.dir {
57 write(w, uint32(directoryHeaderSignature))
58 write(w, h.CreatorVersion)
59 write(w, h.ReaderVersion)
62 write(w, h.ModifiedTime)
63 write(w, h.ModifiedDate)
65 write(w, h.CompressedSize)
66 write(w, h.UncompressedSize)
67 write(w, uint16(len(h.Name)))
68 write(w, uint16(len(h.Extra)))
69 write(w, uint16(len(h.Comment)))
70 write(w, uint16(0)) // disk number start
71 write(w, uint16(0)) // internal file attributes
72 write(w, uint32(0)) // external file attributes
74 writeBytes(w, []byte(h.Name))
75 writeBytes(w, h.Extra)
76 writeBytes(w, []byte(h.Comment))
81 write(w, uint32(directoryEndSignature))
82 write(w, uint16(0)) // disk number
83 write(w, uint16(0)) // disk number where directory starts
84 write(w, uint16(len(w.dir))) // number of entries this disk
85 write(w, uint16(len(w.dir))) // number of entries total
86 write(w, uint32(end-start)) // size of directory
87 write(w, uint32(start)) // start of directory
88 write(w, uint16(0)) // size of comment
90 return w.w.(*bufio.Writer).Flush()
93 // Create adds a file to the zip file using the provided name.
94 // It returns a Writer to which the file contents should be written.
95 // The file's contents must be written to the io.Writer before the next
96 // call to Create, CreateHeader, or Close.
97 func (w *Writer) Create(name string) (io.Writer, os.Error) {
98 header := &FileHeader{
102 return w.CreateHeader(header)
105 // CreateHeader adds a file to the zip file using the provided FileHeader
106 // for the file metadata.
107 // It returns a Writer to which the file contents should be written.
108 // The file's contents must be written to the io.Writer before the next
109 // call to Create, CreateHeader, or Close.
110 func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) {
111 if w.last != nil && !w.last.closed {
112 if err := w.last.close(); err != nil {
117 fh.Flags |= 0x8 // we will write a data descriptor
118 fh.CreatorVersion = 0x14
119 fh.ReaderVersion = 0x14
123 compCount: &countWriter{w: w},
124 crc32: crc32.NewIEEE(),
128 fw.comp = nopCloser{fw.compCount}
130 fw.comp = flate.NewWriter(fw.compCount, 5)
132 return nil, UnsupportedMethod
134 fw.rawCount = &countWriter{w: fw.comp}
138 offset: uint32(w.count),
140 w.dir = append(w.dir, h)
143 if err := writeHeader(w, fh); err != nil {
151 func writeHeader(w io.Writer, h *FileHeader) (err os.Error) {
152 defer recoverError(&err)
153 write(w, uint32(fileHeaderSignature))
154 write(w, h.ReaderVersion)
157 write(w, h.ModifiedTime)
158 write(w, h.ModifiedDate)
160 write(w, h.CompressedSize)
161 write(w, h.UncompressedSize)
162 write(w, uint16(len(h.Name)))
163 write(w, uint16(len(h.Extra)))
164 writeBytes(w, []byte(h.Name))
165 writeBytes(w, h.Extra)
169 type fileWriter struct {
172 rawCount *countWriter
174 compCount *countWriter
179 func (w *fileWriter) Write(p []byte) (int, os.Error) {
181 return 0, os.NewError("zip: write to closed file")
184 return w.rawCount.Write(p)
187 func (w *fileWriter) close() (err os.Error) {
189 return os.NewError("zip: file closed twice")
192 if err = w.comp.Close(); err != nil {
197 fh := w.header.FileHeader
198 fh.CRC32 = w.crc32.Sum32()
199 fh.CompressedSize = uint32(w.compCount.count)
200 fh.UncompressedSize = uint32(w.rawCount.count)
202 // write data descriptor
203 defer recoverError(&err)
204 write(w.zipw, fh.CRC32)
205 write(w.zipw, fh.CompressedSize)
206 write(w.zipw, fh.UncompressedSize)
211 type countWriter struct {
216 func (w *countWriter) Write(p []byte) (int, os.Error) {
217 n, err := w.w.Write(p)
222 type nopCloser struct {
226 func (w nopCloser) Close() os.Error {
230 func write(w io.Writer, data interface{}) {
231 if err := binary.Write(w, binary.LittleEndian, data); err != nil {
236 func writeBytes(w io.Writer, b []byte) {
242 panic(io.ErrShortWrite)