mirror of https://github.com/Superioz/aqua.git
115 lines
2.6 KiB
Go
115 lines
2.6 KiB
Go
package shttp
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/h2non/filetype"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/textproto"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// Upload takes an url and multiple reader, that are used to fill in a multipart form.
|
|
//
|
|
// Heavily inspired by: https://stackoverflow.com/a/20397167/11155150 but with the standard http package,
|
|
// there are not many other ways to do that, so it doesn't really matter.
|
|
func Upload(client *http.Client, url string, values map[string]io.Reader, header map[string]string) (*http.Response, error) {
|
|
// Prepare a form that you will submit to that URL.
|
|
var b bytes.Buffer
|
|
w := multipart.NewWriter(&b)
|
|
for key, r := range values {
|
|
var fw io.Writer
|
|
var err error
|
|
|
|
if x, ok := r.(io.Closer); ok {
|
|
defer x.Close()
|
|
}
|
|
|
|
// Add an image file
|
|
if file, ok := r.(*os.File); ok {
|
|
mime, err := DetectFileType(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fw, err = createFormFile(w, key, file.Name(), mime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
// Add other fields
|
|
fw, err = w.CreateFormField(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Write to form field
|
|
_, err = io.Copy(fw, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
w.Close()
|
|
|
|
req, err := http.NewRequest("POST", url, &b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", w.FormDataContentType())
|
|
for key, val := range header {
|
|
req.Header.Set(key, val)
|
|
}
|
|
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// createFormFile is copied from multipart.CreateFormFile, because it
|
|
// always sets the content type to `application/octet-stream`.
|
|
func createFormFile(w *multipart.Writer, fieldname, filename, contentType string) (io.Writer, error) {
|
|
h := make(textproto.MIMEHeader)
|
|
h.Set("Content-Disposition",
|
|
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
|
|
escapeQuotes(fieldname), escapeQuotes(filename)))
|
|
h.Set("Content-Type", contentType)
|
|
return w.CreatePart(h)
|
|
}
|
|
|
|
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
|
|
|
func escapeQuotes(s string) string {
|
|
return quoteEscaper.Replace(s)
|
|
}
|
|
|
|
// DetectFileType returns the MIME type of given file by reading
|
|
// the file header of the file that is located on disk.
|
|
func DetectFileType(file *os.File) (string, error) {
|
|
f, err := os.Open(file.Name())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
head := make([]byte, 261)
|
|
_, err = f.Read(head)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
kind, err := filetype.Match(head)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if kind == filetype.Unknown {
|
|
return "", fmt.Errorf("unknown file type for %s", file.Name())
|
|
}
|
|
return kind.MIME.Value, nil
|
|
}
|