Skip to content

Instantly share code, notes, and snippets.

@2minchul
Last active September 5, 2021 15:54
Show Gist options
  • Save 2minchul/ee72c5e23207737b7bf8219d9135cab4 to your computer and use it in GitHub Desktop.
Save 2minchul/ee72c5e23207737b7bf8219d9135cab4 to your computer and use it in GitHub Desktop.
get jpg size without full read. written in Golang
package main
import (
"errors"
"io"
"os"
)
type ImageSize struct {
Width int
Height int
}
var InvalidImage = errors.New("invalid image")
func readByte(reader io.Reader) (byte, error) {
b := make([]byte, 1)
_, err := reader.Read(b)
if err != nil {
return 0, err
}
return b[0], nil
}
func readBytes(reader io.Reader, size int) ([]byte, error) {
b := make([]byte, size)
_, err := reader.Read(b)
if err != nil {
return nil, err
}
return b, nil
}
func readUInt16BE(data []byte) int {
return int(data[1]) | (int(data[0]) << 8)
}
func getImageSize(reader io.Reader) (*ImageSize, error) {
data, err := readBytes(reader, 2)
if err != nil {
return nil, err
}
if data[0] != 0xFF || data[1] != 0xD8 {
return nil, InvalidImage
}
for {
code, err := readByte(reader)
if err != nil {
return nil, err
}
if code != 0xFF {
return nil, InvalidImage
}
code, err = readByte(reader)
if err != nil {
return nil, err
}
// skip padding bytes
for code == 0xFF {
code, err = readByte(reader)
if err != nil {
return nil, err
}
}
var length int
// standalone markers, according to JPEG 1992,
// http://www.w3.org/Graphics/JPEG/itu-t81.pdf, see Table B.1
if (0xD0 <= code && code <= 0xD9) || code == 0x01 {
length = 0
} else if 0xC0 <= code && code <= 0xFE {
// the rest of the unreserved markers
data, err = readBytes(reader, 2)
if err != nil {
return nil, err
}
length = readUInt16BE(data) - 2
} else {
// unknown markers
return nil, InvalidImage
}
if code == 0xD9 || code == 0xDA {
// end of the data stream
return nil, InvalidImage
}
data, err = readBytes(reader, length)
if err != nil {
return nil, err
}
if length >= 5 &&
(0xC0 <= code && code <= 0xCF) &&
code != 0xC4 && code != 0xC8 && code != 0xCC {
return &ImageSize{
Width: readUInt16BE(data[3:]),
Height: readUInt16BE(data[1:]),
}, nil
}
}
}
func main() {
f, err := os.Open("example.jpeg")
if err != nil {
panic(err)
}
s, err := getImageSize(f)
if err != nil {
panic(err)
}
println(s.Width, s.Height)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment