2 回答

TA貢獻(xiàn)1853條經(jīng)驗 獲得超18個贊
好吧,我承認(rèn)解決您的任務(wù)的“拼圖”的所有部分都可以在網(wǎng)上找到,這有兩個問題:
他們經(jīng)常錯過某些有趣的細(xì)節(jié)。
有時,他們會給出完全錯誤的建議。
所以,這是一個可行的解決方案。
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"mime"
"mime/multipart"
"net/http"
"net/textproto"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
)
func main() {
const (
dst = "https://api.mapbox.com/styles/v1/AcmeInc/Style_001/sprite"
fname = "path/to/a/sprite/image.svg"
token = "an_invalid_token"
)
err := post(dst, fname, token)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func post(dst, fname, token string) error {
u, err := url.Parse(dst)
if err != nil {
return fmt.Errorf("failed to parse destination url: %w", err)
}
form, err := makeRequestBody(fname)
if err != nil {
return fmt.Errorf("failed to prepare request body: %w", err)
}
q := u.Query()
q.Set("access_token", token)
u.RawQuery = q.Encode()
hdr := make(http.Header)
hdr.Set("Content-Type", form.contentType)
req := http.Request{
Method: "POST",
URL: u,
Header: hdr,
Body: ioutil.NopCloser(form.body),
ContentLength: int64(form.contentLen),
}
resp, err := http.DefaultClient.Do(&req)
if err != nil {
return fmt.Errorf("failed to perform http request: %w", err)
}
defer resp.Body.Close()
_, _ = io.Copy(os.Stdout, resp.Body)
return nil
}
type form struct {
body *bytes.Buffer
contentType string
contentLen int
}
func makeRequestBody(fname string) (form, error) {
ct, err := getImageContentType(fname)
if err != nil {
return form{}, fmt.Errorf(
`failed to get content type for image file "%s": %w`,
fname, err)
}
fd, err := os.Open(fname)
if err != nil {
return form{}, fmt.Errorf("failed to open file to upload: %w", err)
}
defer fd.Close()
stat, err := fd.Stat()
if err != nil {
return form{}, fmt.Errorf("failed to query file info: %w", err)
}
hdr := make(textproto.MIMEHeader)
cd := mime.FormatMediaType("form-data", map[string]string{
"name": "images",
"filename": fname,
})
hdr.Set("Content-Disposition", cd)
hdr.Set("Contnt-Type", ct)
hdr.Set("Content-Length", strconv.FormatInt(stat.Size(), 10))
var buf bytes.Buffer
mw := multipart.NewWriter(&buf)
part, err := mw.CreatePart(hdr)
if err != nil {
return form{}, fmt.Errorf("failed to create new form part: %w", err)
}
n, err := io.Copy(part, fd)
if err != nil {
return form{}, fmt.Errorf("failed to write form part: %w", err)
}
if int64(n) != stat.Size() {
return form{}, fmt.Errorf("file size changed while writing: %s", fd.Name())
}
err = mw.Close()
if err != nil {
return form{}, fmt.Errorf("failed to prepare form: %w", err)
}
return form{
body: &buf,
contentType: mw.FormDataContentType(),
contentLen: buf.Len(),
}, nil
}
var imageContentTypes = map[string]string{
"png": "image/png",
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"svg": "image/svg+xml",
}
func getImageContentType(fname string) (string, error) {
ext := filepath.Ext(fname)
if ext == "" {
return "", fmt.Errorf("file name has no extension: %s", fname)
}
ext = strings.ToLower(ext[1:])
ct, found := imageContentTypes[ext]
if !found {
return "", fmt.Errorf("unknown file name extension: %s", ext)
}
return ct, nil
}
一些關(guān)于實現(xiàn)的隨機注釋可幫助您理解這些概念:
為了構(gòu)造請求的有效負(fù)載(正文),我們使用了一個
bytes.Buffer
實例。
它有一個很好的屬性,指向它的指針 (*bytes.Buffer
) 實現(xiàn)了兩者io.Writer
,io.Reader
因此可以很容易地與處理 I/O 的 Go 標(biāo)準(zhǔn)庫的其他部分組合。在準(zhǔn)備要發(fā)送的多部分表單時,我們不會將整個文件的內(nèi)容吞入內(nèi)存,而是將它們直接“管道”到“多部分表單編寫器”中。
我們有一個查找表,它將要提交的文件名的擴展名映射到其 MIME 類型;我不知道 API 是否需要這樣做;如果不是真的需要,準(zhǔn)備包含文件的表單字段的代碼部分可以簡化很多,但是 cURL 發(fā)送它,我們也是如此。

TA貢獻(xiàn)1869條經(jīng)驗 獲得超4個贊
只是好奇,這是為了什么?
fileContents = bytes.ReplaceAll(fileContents, []byte("\n"), []byte("."))
blockSize := 64
remainder := len(fileContents) % blockSize
iterations := (len(fileContents) - remainder) / blockSize
newBytes := []byte{}
for i := 0; i < iterations; i++ {
start := i * blockSize
end := i*blockSize + blockSize
newBytes = append(newBytes, fileContents[start:end]...)
newBytes = append(newBytes, []byte("\n")...)
}
if remainder > 0 {
newBytes = append(newBytes, fileContents[iterations*blockSize:]...)
newBytes = append(newBytes, []byte("\n")...)
}
if err != nil {
fmt.Printf("Error reading svg file: %v: %v", filePath, err)
continue
}
將整個文件讀入內(nèi)存很少是一個好主意(ioutil.ReadFile)。
正如@muffin-top 所說,那三行代碼怎么樣?
for fileName, filePath := range fileMap {
// h := ...
fw, _ := w.CreatePart(h) // TODO: handle error
f, _ := os.Open(filePath) // TODO: handle error
io.Copy(fw, f) // TODO: handle error
f.Close() // TODO: handle error
}
- 2 回答
- 0 關(guān)注
- 153 瀏覽
添加回答
舉報