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.
18 // A Writer generates multipart messages.
25 // NewWriter returns a new multipart Writer with a random boundary,
27 func NewWriter(w io
.Writer
) *Writer
{
30 boundary
: randomBoundary(),
34 // Boundary returns the Writer's boundary.
35 func (w
*Writer
) Boundary() string {
39 // SetBoundary overrides the Writer's default randomly-generated
40 // boundary separator with an explicit value.
42 // SetBoundary must be called before any parts are created, may only
43 // contain certain ASCII characters, and must be non-empty and
44 // at most 70 bytes long.
45 func (w
*Writer
) SetBoundary(boundary
string) error
{
46 if w
.lastpart
!= nil {
47 return errors
.New("mime: SetBoundary called after write")
49 // rfc2046#section-5.1.1
50 if len(boundary
) < 1 ||
len(boundary
) > 70 {
51 return errors
.New("mime: invalid boundary length")
53 end
:= len(boundary
) - 1
54 for i
, b
:= range boundary
{
55 if 'A' <= b
&& b
<= 'Z' ||
'a' <= b
&& b
<= 'z' ||
'0' <= b
&& b
<= '9' {
59 case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?':
66 return errors
.New("mime: invalid boundary character")
72 // FormDataContentType returns the Content-Type for an HTTP
73 // multipart/form-data with this Writer's Boundary.
74 func (w
*Writer
) FormDataContentType() string {
75 return "multipart/form-data; boundary=" + w
.boundary
78 func randomBoundary() string {
80 _
, err
:= io
.ReadFull(rand
.Reader
, buf
[:])
84 return fmt
.Sprintf("%x", buf
[:])
87 // CreatePart creates a new multipart section with the provided
88 // header. The body of the part should be written to the returned
89 // Writer. After calling CreatePart, any previous part may no longer
91 func (w
*Writer
) CreatePart(header textproto
.MIMEHeader
) (io
.Writer
, error
) {
92 if w
.lastpart
!= nil {
93 if err
:= w
.lastpart
.close(); err
!= nil {
98 if w
.lastpart
!= nil {
99 fmt
.Fprintf(&b
, "\r\n--%s\r\n", w
.boundary
)
101 fmt
.Fprintf(&b
, "--%s\r\n", w
.boundary
)
104 keys
:= make([]string, 0, len(header
))
105 for k
:= range header
{
106 keys
= append(keys
, k
)
109 for _
, k
:= range keys
{
110 for _
, v
:= range header
[k
] {
111 fmt
.Fprintf(&b
, "%s: %s\r\n", k
, v
)
114 fmt
.Fprintf(&b
, "\r\n")
115 _
, err
:= io
.Copy(w
.w
, &b
)
126 var quoteEscaper
= strings
.NewReplacer("\\", "\\\\", `"`, "\\\"")
128 func escapeQuotes(s
string) string {
129 return quoteEscaper
.Replace(s
)
132 // CreateFormFile is a convenience wrapper around CreatePart. It creates
133 // a new form-data header with the provided field name and file name.
134 func (w
*Writer
) CreateFormFile(fieldname
, filename
string) (io
.Writer
, error
) {
135 h
:= make(textproto
.MIMEHeader
)
136 h
.Set("Content-Disposition",
137 fmt
.Sprintf(`form-data; name="%s"; filename="%s"`,
138 escapeQuotes(fieldname
), escapeQuotes(filename
)))
139 h
.Set("Content-Type", "application/octet-stream")
140 return w
.CreatePart(h
)
143 // CreateFormField calls CreatePart with a header using the
145 func (w
*Writer
) CreateFormField(fieldname
string) (io
.Writer
, error
) {
146 h
:= make(textproto
.MIMEHeader
)
147 h
.Set("Content-Disposition",
148 fmt
.Sprintf(`form-data; name="%s"`, escapeQuotes(fieldname
)))
149 return w
.CreatePart(h
)
152 // WriteField calls CreateFormField and then writes the given value.
153 func (w
*Writer
) WriteField(fieldname
, value
string) error
{
154 p
, err
:= w
.CreateFormField(fieldname
)
158 _
, err
= p
.Write([]byte(value
))
162 // Close finishes the multipart message and writes the trailing
163 // boundary end line to the output.
164 func (w
*Writer
) Close() error
{
165 if w
.lastpart
!= nil {
166 if err
:= w
.lastpart
.close(); err
!= nil {
171 _
, err
:= fmt
.Fprintf(w
.w
, "\r\n--%s--\r\n", w
.boundary
)
178 we error
// last error that occurred writing
181 func (p
*part
) close() error
{
186 func (p
*part
) Write(d
[]byte) (n
int, err error
) {
188 return 0, errors
.New("multipart: can't write to finished part")
190 n
, err
= p
.mw
.w
.Write(d
)