// Copyright 2011 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package multipartimport ()// A Writer generates multipart messages.typeWriterstruct { w io.Writer boundary string lastpart *part}// NewWriter returns a new multipart [Writer] with a random boundary,// writing to w.func ( io.Writer) *Writer {return &Writer{w: ,boundary: randomBoundary(), }}// Boundary returns the [Writer]'s boundary.func ( *Writer) () string {return .boundary}// SetBoundary overrides the [Writer]'s default randomly-generated// boundary separator with an explicit value.//// SetBoundary must be called before any parts are created, may only// contain certain ASCII characters, and must be non-empty and// at most 70 bytes long.func ( *Writer) ( string) error {if .lastpart != nil {returnerrors.New("mime: SetBoundary called after write") }// rfc2046#section-5.1.1iflen() < 1 || len() > 70 {returnerrors.New("mime: invalid boundary length") } := len() - 1for , := range {if'A' <= && <= 'Z' || 'a' <= && <= 'z' || '0' <= && <= '9' {continue }switch {case'\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?':continuecase' ':if != {continue } }returnerrors.New("mime: invalid boundary character") } .boundary = returnnil}// FormDataContentType returns the Content-Type for an HTTP// multipart/form-data with this [Writer]'s Boundary.func ( *Writer) () string { := .boundary// We must quote the boundary if it contains any of the // tspecials characters defined by RFC 2045, or space.ifstrings.ContainsAny(, `()<>@,;:\"/[]?= `) { = `"` + + `"` }return"multipart/form-data; boundary=" + }func randomBoundary() string {var [30]byte , := io.ReadFull(rand.Reader, [:])if != nil {panic() }returnfmt.Sprintf("%x", [:])}// CreatePart creates a new multipart section with the provided// header. The body of the part should be written to the returned// [Writer]. After calling CreatePart, any previous part may no longer// be written to.func ( *Writer) ( textproto.MIMEHeader) (io.Writer, error) {if .lastpart != nil {if := .lastpart.close(); != nil {returnnil, } }varbytes.Bufferif .lastpart != nil {fmt.Fprintf(&, "\r\n--%s\r\n", .boundary) } else {fmt.Fprintf(&, "--%s\r\n", .boundary) }for , := rangeslices.Sorted(maps.Keys()) {for , := range [] {fmt.Fprintf(&, "%s: %s\r\n", , ) } }fmt.Fprintf(&, "\r\n") , := io.Copy(.w, &)if != nil {returnnil, } := &part{mw: , } .lastpart = return , nil}var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")func escapeQuotes( string) string {returnquoteEscaper.Replace()}// CreateFormFile is a convenience wrapper around [Writer.CreatePart]. It creates// a new form-data header with the provided field name and file name.func ( *Writer) (, string) (io.Writer, error) { := make(textproto.MIMEHeader) .Set("Content-Disposition",fmt.Sprintf(`form-data; name="%s"; filename="%s"`,escapeQuotes(), escapeQuotes())) .Set("Content-Type", "application/octet-stream")return .CreatePart()}// CreateFormField calls [Writer.CreatePart] with a header using the// given field name.func ( *Writer) ( string) (io.Writer, error) { := make(textproto.MIMEHeader) .Set("Content-Disposition",fmt.Sprintf(`form-data; name="%s"`, escapeQuotes()))return .CreatePart()}// WriteField calls [Writer.CreateFormField] and then writes the given value.func ( *Writer) (, string) error { , := .CreateFormField()if != nil {return } _, = .Write([]byte())return}// Close finishes the multipart message and writes the trailing// boundary end line to the output.func ( *Writer) () error {if .lastpart != nil {if := .lastpart.close(); != nil {return } .lastpart = nil } , := fmt.Fprintf(.w, "\r\n--%s--\r\n", .boundary)return}type part struct { mw *Writer closed bool we error// last error that occurred writing}func ( *part) () error { .closed = truereturn .we}func ( *part) ( []byte) ( int, error) {if .closed {return0, errors.New("multipart: can't write to finished part") } , = .mw.w.Write()if != nil { .we = }return}
The pages are generated with Goldsv0.7.3-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.