mirror of
https://github.com/dsoprea/go-exif.git
synced 2025-05-31 11:41:57 +00:00
Given a stream of data, it is possible to determine the beginning of EXIF data but not the end. Therefore, either an image-aware implementation must know how to parse an image and extract the EXIF data or a brute-force search implementation (one of which is provided by this project) must find the start anchor and then return all bytes from that to the end of the file. We have been made aware of some use-cases where a brute-force search might be unavoidable due to trust or stability issues with the image structure. This leads to large allocations. This can be avoided by accomodating support that will allow for both a byte-slice or an `io.ReadSeeker`. Since the EXIF structure is typically not read- intensive (a couple of kilobytes if no thumbnail is present), this should have a minimal performance impact. Closes #42
51 lines
1.1 KiB
Go
51 lines
1.1 KiB
Go
package exif
|
|
|
|
import (
|
|
"io"
|
|
|
|
"github.com/dsoprea/go-logging"
|
|
"github.com/dsoprea/go-utility/filesystem"
|
|
)
|
|
|
|
type ExifBlobSeeker interface {
|
|
GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error)
|
|
}
|
|
|
|
// ExifReadSeeker knows how to retrieve data from the EXIF blob relative to the
|
|
// beginning of the blob (so, absolute position (0) is the first byte of the
|
|
// EXIF data).
|
|
type ExifReadSeeker struct {
|
|
rs io.ReadSeeker
|
|
}
|
|
|
|
func NewExifReadSeeker(rs io.ReadSeeker) *ExifReadSeeker {
|
|
return &ExifReadSeeker{
|
|
rs: rs,
|
|
}
|
|
}
|
|
|
|
func NewExifReadSeekerWithBytes(exifData []byte) *ExifReadSeeker {
|
|
sb := rifs.NewSeekableBufferWithBytes(exifData)
|
|
edbs := NewExifReadSeeker(sb)
|
|
|
|
return edbs
|
|
}
|
|
|
|
// Fork creates a new ReadSeeker instead that wraps a BouncebackReader to
|
|
// maintain its own position in the stream.
|
|
func (edbs *ExifReadSeeker) GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error) {
|
|
defer func() {
|
|
if state := recover(); state != nil {
|
|
err = log.Wrap(state.(error))
|
|
}
|
|
}()
|
|
|
|
br, err := rifs.NewBouncebackReader(edbs.rs)
|
|
log.PanicIf(err)
|
|
|
|
_, err = br.Seek(initialOffset, io.SeekStart)
|
|
log.PanicIf(err)
|
|
|
|
return br, nil
|
|
}
|