go 图片上传:文件和Base64方式

go 图片上传:文件和Base64方式

参考:https://www.sobyte.net/post/2022-03/go-multipart-form-data/

网上找了好些教程,go客户端上传文件这一块,大部分的例子都是从文件读取然后上传的,但是项目中因为调用了第三方SDK,返回的是 base64 格式的图片,却没找到这方面的例子,特此分享一下解决方案,避免已经拿到 base64 图片数据,却还要写一遍文件上传这种低效的行为

文件上传

go客户端

client.go:

var (
    filePath string
    addr     string
)

func init() {
    flag.StringVar(&filePath, "file", "", "the file to upload")
    flag.StringVar(&addr, "addr", "localhost:8080", "the addr of file server")
    flag.Parse()
}

func main() {
    if filePath == "" {
        fmt.Println("file must not be empty")
        return
    }

    err := doUpload(addr, filePath)
    if err != nil {
        fmt.Printf("upload file [%s] error: %s", filePath, err)
        return
    }
    fmt.Printf("upload file [%s] ok\n", filePath)
}

func createReqBody(filePath string) (string, io.Reader, error) {
    var err error

    buf := new(bytes.Buffer)
    bw := multipart.NewWriter(buf) // body writer

    f, err := os.Open(filePath)
    if err != nil {
        return "", nil, err
    }
    defer f.Close()

    // text part1
    p1w, _ := bw.CreateFormField("name")
    p1w.Write([]byte("Tony Bai"))

    // text part2
    p2w, _ := bw.CreateFormField("age")
    p2w.Write([]byte("15"))

    // file part1
    _, fileName := filepath.Split(filePath)
    fw1, _ := bw.CreateFormFile("file1", fileName)
    io.Copy(fw1, f)

    bw.Close() //write the tail boundry
    return bw.FormDataContentType(), buf, nil
}

func doUpload(addr, filePath string) error {
    // create body
    contType, reader, err := createReqBody(filePath)
    if err != nil {
        return err
    }

    url := fmt.Sprintf("http://%s/upload", addr)
    req, err := http.NewRequest("POST", url, reader)

    // add headers
    req.Header.Add("Content-Type", contType)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("request send error:", err)
        return err
    }
    resp.Body.Close()
    return nil
}

go服务端

server.go:

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

const uploadPath = "./upload"

func handleUploadFile(w http.ResponseWriter, r *http.Request) {
    r.ParseMultipartForm(100)
    mForm := r.MultipartForm

    for k, _ := range mForm.File {
        // k is the key of file part
        file, fileHeader, err := r.FormFile(k)
        if err != nil {
            fmt.Println("inovke FormFile error:", err)
            return
        }
        defer file.Close()
        fmt.Printf("the uploaded file: name[%s], size[%d], header[%#v]\n",
            fileHeader.Filename, fileHeader.Size, fileHeader.Header)

        // store uploaded file into local path
        localFileName := uploadPath + "/" + fileHeader.Filename
        out, err := os.Create(localFileName)
        if err != nil {
            fmt.Printf("failed to open the file %s for writing", localFileName)
            return
        }
        defer out.Close()
        _, err = io.Copy(out, file)
        if err != nil {
            fmt.Printf("copy file err:%s\n", err)
            return
        }
        fmt.Printf("file %s uploaded ok\n", fileHeader.Filename)
    }
}

func main() {
    http.HandleFunc("/upload", handleUploadFile)
    http.ListenAndServe(":8080", nil)
}

Base64从内存上传

如果不从文件读取,而是直接 Base64解码(假设调用了别人的SDK),从内存中上传应该怎么实现呢?

把这个地方由:

func createReqBody(filePath string) (string, io.Reader, error) {
    // ...
    fw1, _ := bw.CreateFormFile("file1", fileName)
    io.Copy(fw1, f)
    // ...
}

改成:

func (b botUi) createReqBody(reader io.Reader) (string, *bytes.Buffer, error) {
    // ...
    
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			escapeQuotes("image"), escapeQuotes("image.jpeg")))
	h.Set("Content-Type", "image/jpeg")

	// w, err := writer.CreateFormFile("image", "image.jpeg")
	w, err := writer.CreatePart(h)
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
func escapeQuotes(s string) string {
	return quoteEscaper.Replace(s)
}

reader 也就是从内存读取图片的流,解码base64时获得:

func DecodePng(base64Png string) (*bytes.Buffer, error) {
	out := bytes.NewBuffer([]byte{})
	unBased, err := base64.StdEncoding.DecodeString(base64Png)
	if err != nil {
		return nil, err
	}
	im, err := png.Decode(bytes.NewReader(unBased))
	if err != nil {
		return nil, err
	}
	if err = png.Encode(out, im); err != nil {
		return nil, err
	}
	return out, nil
}

*bytes.Buffer,这个类型实现了 Read() 接口。

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值