I'm struggling to understand what's happening if I chain a Peek
and a Reset
. Here are two versions of a Thumbnail
function:
func Thumbnail(r io.Reader, w io.Writer, width int) error {
_, err := jpeg.Decode(r)
if err != nil {
fmt.Println("Error decoding image: ", err)
return err
}
return nil
}
This works. The data is read successfully into a jpeg image. Now, this second version doesn't work:
func Thumbnail2(r io.Reader, w io.Writer, width int) error {
var err error
buf := bufio.NewReader(r)
headerBytes, err := buf.Peek(512)
if err != nil {
fmt.Println("Error reading header bytes: ", err)
return err
}
mimetype := detectContentType(headerBytes)
fmt.Println("MIME type detected: ", mimetype)
buf.Reset(r) // I tried with and without, doesn't work
_, err = jpeg.Decode(r)
if err != nil {
fmt.Println("Error decoding image: ", err)
return err
}
return nil
}
I understand that io.Reader
is an interface to read a stream of bytes. I understand that since it's a Reader
, it doesn't have the Seek
interface (which would have saved me some troubles). My intention was to wrap the original io.Reader
into a bufio
reader, so I could have a look at the first 512 bytes, then read the data as planned in the call to jpeg.Decode
. But that doesn't work, with or without the call to Reset
. I'm getting this error:
invalid JPEG format: missing SOI marker
I don't understand why this is happening. Could you enlighten me please?
You're on the right track with buf.Peek()
but buf.Reset()
is not the right approach, as it clears buffers but doesn't magically rewind the reader r
. Instead, you can use the fact that buf.Peek()
doesn't affect future normal buf.Read()
's, and pass buf
directly to the JPEG decoder. The data read from r
to fill the buffer during buf.Peek()
will be the first data fed to the JPEG decoder. You should also allow Peek
to return fewer than 512 bytes, to support small JPEG's.
package main
import (
"bufio"
"bytes"
"encoding/base64"
"fmt"
"image/jpeg"
"io"
)
func detectContentType(bytes []byte) string {
return "image/jpeg"
}
func Thumbnail2(r io.Reader, w io.Writer, width int) error {
var err error
buf := bufio.NewReader(r)
headerBytes, err := buf.Peek(512)
if len(headerBytes) == 0 && err != nil {
fmt.Println("Error reading header bytes: ", err)
return err
}
mimetype := detectContentType(headerBytes)
fmt.Println("MIME type detected: ", mimetype)
_, err = jpeg.Decode(buf)
if err != nil {
fmt.Println("Error decoding image: ", err)
return err
}
return nil
}
func main() {
// 16x16 white JPEG
b64 := "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAQABADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD36iiigD//2Q=="
body := make([]byte, base64.StdEncoding.DecodedLen(len(b64)))
_, err := base64.StdEncoding.Decode(body, []byte(b64))
if err != nil {
panic(err)
}
fmt.Println(Thumbnail2(bytes.NewReader(body), nil, 16))
}