io.Reader遊標引起的血案

背景

線上運行了一個圖片合成程序,默認的小程序二維碼中獎是小程序LOGO,不知足需求,因此將微信小程序二維碼和用戶頭像合成在一張圖片。
因爲微信圖片有時候返回的Content-Type不對應(好比內容是PNG的,頭確是image/jpeg)因此使用jpeg/png/gif的順序進行圖片數據解析,哪一個成功就返回解析結果。golang

問題

老是出現諸如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前幾個字節以下:code

[]byte{0xff, 0xd8, 0xff, 0xe0, 0x0, 0x10}

能夠看到第1個字節和第2個字節知足要求,按理說不會出現這個問題,無奈只能求助於Google,搜索了
invalid JPEG format: missing SOI marker關鍵字出現一篇Covert base64 string to JPG引發了個人注意。
打開看到答案orm

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的源碼,發現遊標讀取完後並未重置get

// 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就是遊標了。問題圓滿解決string

相關文章
相關標籤/搜索