線上運行了一個圖片合成程序,默認的小程序二維碼中獎是小程序LOGO,不滿足需求,所以將微信小程序二維碼和用戶頭像合成在一張圖片。
由于微信圖片有時候返回的Content-Type不對應(比如內容是PNG的,頭確是image/jpeg)所以使用jpeg/png/gif的順序進行圖片數據解析,哪個成功就返回解析結果。
總是出現諸如 invalid JPEG format: missing SOI marker
我去查看jpeg.Decode的源碼,如下:
func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { d.r = r // Check for the Start Of Image marker. if err := d.readFull(d.tmp[:2]); err != nil { return nil, err } if d.tmp[0] != 0xff || d.tmp[1] != soiMarker { return nil, FormatError("missing SOI marker") } ... |
soiMarker常量
soiMarker = 0xd8 // Start Of Image.
可以看到判斷了第1個字節如果不是 0xff 或者第2個字節不是 0xd8 就報錯。打印圖片的bytes前幾個字節如下:
[]byte{0xff, 0xd8, 0xff, 0xe0, 0x0, 0x10}
可以看到第1個字節和第2個字節滿足要求,按理說不會出現這個問題,無奈只能求助于Google,搜索了
invalid JPEG format: missing SOI marker 關鍵字出現一篇 Covert base64 string to JPG 引起了我的注意。
打開看到答案
You need to create a new reader for each decoder:
pngI, errPng := png.Decode(bytes.NewReader(unbased)) // ... jpgI, errJpg := jpeg.Decode(bytes.NewReader(unbased))
原來需要重新創建讀取器,重新創建讀取器后問題解決。
抱著打破砂鍋問到底的心態,查看了一下 bytes.Reader 的源碼,發現游標讀取完后并未重置
// Read implements the io.Reader interface. func (r *Reader) Read(b []byte) (n int, err error) { if r.i >= int64(len(r.s)) { return 0, io.EOF } r.prevRune = -1 n = copy(b, r.s[r.i:]) r.i += int64(n) return } |
Reader定義
type Reader struct { s []byte i int64 // current reading index prevRune int // index of previous rune; or < 0 }
可以看到 r.i 就是游標了。問題圓滿解決