第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Golang 多部分文件表單請求

Golang 多部分文件表單請求

Go
胡子哥哥 2022-06-27 15:09:48
我正在針對 Mapbox 編寫一個 API 客戶端,將一批 svg 圖像上傳到自定義地圖。他們?yōu)榇颂峁┑?api 記錄在一個可以正常工作的示例 cUrl 調(diào)用中:curl -F images=@include/mapbox/sprites_dark/aubergine_selected.svg "https://api.mapbox.com/styles/v1/<my_company>/<my_style_id>/sprite?access_token=$MAPBOX_API_KEY" --trace-ascii /dev/stdout當(dāng)嘗試從 golang 做同樣的事情時,我很快發(fā)現(xiàn) multiform 庫非常有限,并編寫了一些代碼來發(fā)出類似于上面提到的 cUrl 請求的請求。func createMultipartFormData(fileMap map[string]string) (bytes.Buffer, *multipart.Writer) {    var b bytes.Buffer    var err error    w := multipart.NewWriter(&b)    var fw io.Writer    for fileName, filePath := range fileMap {        h := make(textproto.MIMEHeader)        h.Set("Content-Disposition",            fmt.Sprintf(`form-data; name="%s"; filename="%s"`, "images", fileName))        h.Set("Content-Type", "image/svg+xml")        if fw, err = w.CreatePart(h); err != nil {            fmt.Printf("Error creating form File %v, %v", fileName, err)            continue        }        fileContents, err := ioutil.ReadFile(filePath)        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        }甚至想盡可能限制行長并復(fù)制 cUrl 使用的編碼,但到目前為止還沒有運氣。有經(jīng)驗的人知道為什么這適用于 cUrl 而不是 golang?
查看完整描述

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ā)送它,我們也是如此。


查看完整回答
反對 回復(fù) 2022-06-27
?
MMTTMM

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

    }


查看完整回答
反對 回復(fù) 2022-06-27
  • 2 回答
  • 0 關(guān)注
  • 153 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號