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 // A Writer generates multipart messages.
24 // NewWriter returns a new multipart Writer with a random boundary,
26 func NewWriter(w io.Writer) *Writer {
29 boundary: randomBoundary(),
33 // Boundary returns the Writer's randomly selected boundary string.
34 func (w *Writer) Boundary() string {
38 // FormDataContentType returns the Content-Type for an HTTP
39 // multipart/form-data with this Writer's Boundary.
40 func (w *Writer) FormDataContentType() string {
41 return "multipart/form-data; boundary=" + w.boundary
44 func randomBoundary() string {
46 _, err := io.ReadFull(rand.Reader, buf[:])
50 return fmt.Sprintf("%x", buf[:])
53 // CreatePart creates a new multipart section with the provided
54 // header. The body of the part should be written to the returned
55 // Writer. After calling CreatePart, any previous part may no longer
57 func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, os.Error) {
58 if w.lastpart != nil {
59 if err := w.lastpart.close(); err != nil {
64 if w.lastpart != nil {
65 fmt.Fprintf(&b, "\r\n--%s\r\n", w.boundary)
67 fmt.Fprintf(&b, "--%s\r\n", w.boundary)
69 // TODO(bradfitz): move this to textproto.MimeHeader.Write(w), have it sort
70 // and clean, like http.Header.Write(w) does.
71 for k, vv := range header {
72 for _, v := range vv {
73 fmt.Fprintf(&b, "%s: %s\r\n", k, v)
76 fmt.Fprintf(&b, "\r\n")
77 _, err := io.Copy(w.w, &b)
88 func escapeQuotes(s string) string {
89 s = strings.Replace(s, "\\", "\\\\", -1)
90 s = strings.Replace(s, "\"", "\\\"", -1)
94 // CreateFormFile is a convenience wrapper around CreatePart. It creates
95 // a new form-data header with the provided field name and file name.
96 func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, os.Error) {
97 h := make(textproto.MIMEHeader)
98 h.Set("Content-Disposition",
99 fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
100 escapeQuotes(fieldname), escapeQuotes(filename)))
101 h.Set("Content-Type", "application/octet-stream")
102 return w.CreatePart(h)
105 // CreateFormField calls CreatePart with a header using the
107 func (w *Writer) CreateFormField(fieldname string) (io.Writer, os.Error) {
108 h := make(textproto.MIMEHeader)
109 h.Set("Content-Disposition",
110 fmt.Sprintf(`form-data; name="%s"`, escapeQuotes(fieldname)))
111 return w.CreatePart(h)
114 // WriteField calls CreateFormField and then writes the given value.
115 func (w *Writer) WriteField(fieldname, value string) os.Error {
116 p, err := w.CreateFormField(fieldname)
120 _, err = p.Write([]byte(value))
124 // Close finishes the multipart message and writes the trailing
125 // boundary end line to the output.
126 func (w *Writer) Close() os.Error {
127 if w.lastpart != nil {
128 if err := w.lastpart.close(); err != nil {
133 _, err := fmt.Fprintf(w.w, "\r\n--%s--\r\n", w.boundary)
140 we os.Error // last error that occurred writing
143 func (p *part) close() os.Error {
148 func (p *part) Write(d []byte) (n int, err os.Error) {
150 return 0, os.NewError("multipart: can't write to finished part")
152 n, err = p.mw.w.Write(d)