github.com/diamondburned/arikawa/v2@v2.1.0/utils/sendpart/sendpart.go (about)

     1  package sendpart
     2  
     3  import (
     4  	"io"
     5  	"mime/multipart"
     6  	"net/url"
     7  	"strconv"
     8  
     9  	"github.com/diamondburned/arikawa/v2/utils/httputil"
    10  	"github.com/diamondburned/arikawa/v2/utils/json"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // File represents a file to be uploaded to Discord.
    15  type File struct {
    16  	Name   string
    17  	Reader io.Reader
    18  }
    19  
    20  // AttachmentURI returns the file encoded using the attachment URI required for
    21  // embedding an attachment image.
    22  func (f File) AttachmentURI() string {
    23  	u := url.URL{
    24  		Scheme: "attachment",
    25  		Path:   f.Name,
    26  	}
    27  	return u.String()
    28  }
    29  
    30  // DataMultipartWriter is a MultipartWriter that also contains data that's
    31  // JSON-marshalable.
    32  type DataMultipartWriter interface {
    33  	// NeedsMultipart returns true if the data interface must be sent using
    34  	// multipart form.
    35  	NeedsMultipart() bool
    36  
    37  	httputil.MultipartWriter
    38  }
    39  
    40  // POST sends a POST request using client to the given URL and unmarshal the
    41  // body into v if it's not nil. It will only send using multipart if files is
    42  // true.
    43  func POST(c *httputil.Client, data DataMultipartWriter, v interface{}, url string) error {
    44  	if !data.NeedsMultipart() {
    45  		// No files, so no need for streaming.
    46  		return c.RequestJSON(v, "POST", url, httputil.WithJSONBody(data))
    47  	}
    48  
    49  	resp, err := c.MeanwhileMultipart(data, "POST", url)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	var body = resp.GetBody()
    55  	defer body.Close()
    56  
    57  	if v == nil {
    58  		return nil
    59  	}
    60  
    61  	return json.DecodeStream(body, v)
    62  }
    63  
    64  // Write writes the item into payload_json and the list of files into the
    65  // multipart writer. Write does not close the body.
    66  func Write(body *multipart.Writer, item interface{}, files []File) error {
    67  	// Encode the JSON body first
    68  	w, err := body.CreateFormField("payload_json")
    69  	if err != nil {
    70  		return errors.Wrap(err, "failed to create bodypart for JSON")
    71  	}
    72  
    73  	if err := json.EncodeStream(w, item); err != nil {
    74  		return errors.Wrap(err, "failed to encode JSON")
    75  	}
    76  
    77  	for i, file := range files {
    78  		num := strconv.Itoa(i)
    79  
    80  		w, err := body.CreateFormFile("file"+num, file.Name)
    81  		if err != nil {
    82  			return errors.Wrap(err, "failed to create bodypart for "+num)
    83  		}
    84  
    85  		if _, err := io.Copy(w, file.Reader); err != nil {
    86  			return errors.Wrap(err, "failed to write for file "+num)
    87  		}
    88  	}
    89  
    90  	return nil
    91  }