前言

需要批量上传图片到图床,之前用python写脚本倒是很简单,不过GO因为并发太强大了,所以这次用GO来试试

基础版

构建一个上传图片的请求,主要代码在imgkr()函数中

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "golang.org/x/net/publicsuffix"
  6. "io"
  7. "math/rand"
  8. "mime/multipart"
  9. "net/http"
  10. "net/http/cookiejar"
  11. "os"
  12. "path/filepath"
  13. )
  14. var client http.Client
  15. func imgkr() {
  16. // 要上传的文件
  17. imgFilePath := "/Users/d4m1ts/d4m1ts/Upload/logo.png"
  18. file, _ := os.Open(imgFilePath)
  19. defer file.Close()
  20. // 设置body数据并写入缓冲区
  21. bodyBuff := bytes.NewBufferString("") //bodyBuff := &bytes.Buffer{}
  22. bodyWriter := multipart.NewWriter(bodyBuff)
  23. _ = bodyWriter.SetBoundary(fmt.Sprintf("-----------------------------%d", rand.Int()))
  24. // 加入图片二进制
  25. part, _ := bodyWriter.CreateFormFile("source", filepath.Base(file.Name()))
  26. _, _ = io.Copy(part, file)
  27. // 其他字段
  28. _ = bodyWriter.WriteField("a", "b")
  29. // 自动补充boundary结尾
  30. bodyWriter.Close()
  31. fmt.Println(bodyBuff) //构建的完整数据包,没有header
  32. // 组合创建数据包
  33. req, _ := http.NewRequest("POST", "http://127.0.0.1:8000", bodyBuff)
  34. req.ContentLength = int64(bodyBuff.Len())
  35. req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
  36. req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
  37. client.Do(req)
  38. }
  39. func main() {
  40. jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
  41. client = http.Client{Jar: jar}
  42. imgkr()
  43. }

接收到的数据包
06.使用GO上传图片 - 图1

进阶版

上面的数据包大概是没什么问题,但是图片的Content-Typeapplication/octet-stream,而一般情况下图片的应该是形如image/png
分析了一下原因,是因为CreateFormFile函数里面硬编码了
06.使用GO上传图片 - 图2
看里面的一些函数,也没找到直接设置这个地方的Content-Type的,所以我决定根据他的规则重新写一下。


分析这个函数,前面h是header头,也是我们要修改的地方,后面是调用*Writer.CreatePart,我们也模拟这个行为就可以了
所以微调一下代码(直接给代码复制粘贴过来改就可以了,也可以直接重写CreateFormFile函数,后者稍微麻烦一点点)
06.使用GO上传图片 - 图3
效果
06.使用GO上传图片 - 图4
目标达成,完整代码

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "golang.org/x/net/publicsuffix"
  6. "io"
  7. "math/rand"
  8. "mime/multipart"
  9. "net/http"
  10. "net/http/cookiejar"
  11. "net/textproto"
  12. "os"
  13. "path/filepath"
  14. "strings"
  15. )
  16. var client http.Client
  17. var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
  18. func escapeQuotes(s string) string {
  19. return quoteEscaper.Replace(s)
  20. }
  21. func imgkr() {
  22. // 要上传的文件
  23. imgFilePath := "/Users/d4m1ts/d4m1ts/Upload/logo.png"
  24. file, _ := os.Open(imgFilePath)
  25. defer file.Close()
  26. // 设置body数据并写入缓冲区
  27. bodyBuff := bytes.NewBufferString("") //bodyBuff := &bytes.Buffer{}
  28. bodyWriter := multipart.NewWriter(bodyBuff)
  29. _ = bodyWriter.SetBoundary(fmt.Sprintf("-----------------------------%d", rand.Int()))
  30. // 加入图片二进制
  31. h := make(textproto.MIMEHeader)
  32. h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, escapeQuotes("source"), escapeQuotes(filepath.Base(file.Name()))))
  33. h.Set("Content-Type", "image/png")
  34. part, _ := bodyWriter.CreatePart(h)
  35. _, _ = io.Copy(part, file)
  36. // 其他字段
  37. _ = bodyWriter.WriteField("a", "b")
  38. // 自动补充boundary结尾
  39. bodyWriter.Close()
  40. fmt.Println(bodyBuff) //构建的完整数据包,没有header
  41. // 组合创建数据包
  42. req, _ := http.NewRequest("POST", "http://127.0.0.1:8000", bodyBuff)
  43. req.ContentLength = int64(bodyBuff.Len())
  44. req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
  45. req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
  46. client.Do(req)
  47. }
  48. func main() {
  49. jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
  50. client = http.Client{Jar: jar}
  51. imgkr()
  52. }

完整版

写了一个上传到https://imgtu.com/的,大家有需可以参考
为了方便以后的复制粘贴,给构造上传图片请求单独写到imgkr()函数中的,不然应该给结果解析也写里面的

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "golang.org/x/net/publicsuffix"
  6. "io"
  7. "io/ioutil"
  8. "math/rand"
  9. "mime/multipart"
  10. "net/http"
  11. "net/http/cookiejar"
  12. "net/textproto"
  13. "os"
  14. "path/filepath"
  15. "regexp"
  16. "strings"
  17. "time"
  18. "github.com/json-iterator/go"
  19. )
  20. var client http.Client
  21. var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
  22. func escapeQuotes(s string) string {
  23. return quoteEscaper.Replace(s)
  24. }
  25. /*
  26. 获取上传图片需要的token
  27. */
  28. func getToken() string {
  29. u := "https://imgtu.com/"
  30. req, _ := http.NewRequest("GET", u, nil)
  31. req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
  32. resp, _ := client.Do(req)
  33. res,_ := ioutil.ReadAll(resp.Body)
  34. regex, _ := regexp.Compile("PF.obj.config.auth_token = \"(.*?)\";")
  35. if regex.MatchString(string(res)){
  36. token := regex.FindStringSubmatch(string(res))[1]
  37. return token
  38. }
  39. return ""
  40. }
  41. /*
  42. 构造上传请求
  43. */
  44. func imgkr(token string, imgFilePath string) *http.Request {
  45. // 上传网址
  46. target := "https://imgtu.com/json"
  47. // 要上传的文件
  48. file, _ := os.Open(imgFilePath)
  49. defer file.Close()
  50. // 设置body数据并写入缓冲区
  51. bodyBuff := bytes.NewBufferString("") //bodyBuff := &bytes.Buffer{}
  52. bodyWriter := multipart.NewWriter(bodyBuff)
  53. _ = bodyWriter.SetBoundary(fmt.Sprintf("-----------------------------%d", rand.Int()))
  54. // 加入图片二进制
  55. h := make(textproto.MIMEHeader)
  56. h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, escapeQuotes("source"), escapeQuotes(filepath.Base(file.Name()))))
  57. h.Set("Content-Type", "image/png")
  58. part, _ := bodyWriter.CreatePart(h)
  59. _, _ = io.Copy(part, file)
  60. // 其他字段
  61. _ = bodyWriter.WriteField("type", "file")
  62. _ = bodyWriter.WriteField("action", "upload")
  63. _ = bodyWriter.WriteField("timestamp", fmt.Sprintf("%d", time.Now().UnixNano()/1e6))
  64. _ = bodyWriter.WriteField("auth_token", token)
  65. _ = bodyWriter.WriteField("nsfw", "0")
  66. // 自动补充boundary结尾
  67. _ = bodyWriter.Close()
  68. //fmt.Println(bodyBuff) //构建的完整数据包,没有header
  69. // 创建请求
  70. req, _ := http.NewRequest("POST", target, bodyBuff)
  71. req.ContentLength = int64(bodyBuff.Len())
  72. req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
  73. req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
  74. return req
  75. }
  76. func main() {
  77. jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
  78. client = http.Client{Jar: jar}
  79. token := getToken()
  80. req := imgkr(token, "/Users/d4m1ts/d4m1ts/Upload/logo.png")
  81. resp, _ := client.Do(req)
  82. res, _ := ioutil.ReadAll(resp.Body)
  83. imageUrl := jsoniter.Get(res, "image").Get("url")
  84. fmt.Println(imageUrl.ToString())
  85. }

效果
06.使用GO上传图片 - 图5