Search code examples
goexif

how to read exif data and decode the image from same io.Reader


I'm writing a go application that uses gqlgen for graphQL API.

when a user uploads a file using the api I get it as a type of graphql.Upload

type Upload struct {
    File        io.Reader
    Filename    string
    Size        int64
    ContentType string
}

I want to be able to load the exif data of the image, decode the image and rotate it based on the exif orientation.

I use github.com/rwcarlsen/goexif/exif to fetch the exif information and github.com/disintegration/imaging for rotation, but I cannot open graphql.Upload.File twice. it fails the 2hd time.

func updateImage(dir string, id int, imgFile *graphql.Upload) error {
    image := dbimage.GetImagePathNoTemplate(dir, id)
    imageThumbnail := dbimage.GetImageThumbnailPathNoTemplate(dir, id)
    var myImage image2.Image
    var err error
    switch imgFile.ContentType {
    case "image/png":
        if myImage, err = png.Decode(imgFile.File); err != nil {
            return err
        }
        break
    case "image/jpeg":
        if myImage, err = jpeg.Decode(imgFile.File); err != nil {
            return err
        }
        break
    case "image/webp":
        if myImage, err = webpbin.Decode(imgFile.File); err != nil {
            return err
        }
        break

    default:
        return errors.Errorf("unknown image mimetype %v", imgFile.ContentType)
    }
   FAILS HERE:  metaData, err := exif.Decode(imgFile.File)
   ...
}

of course if I extract the exif first and then decode the image, then the image decode fails.

I don't get a full path to the file and I get only one io.Reader. what can I do to fetch both exif and decode the image ?

thanks


Solution

  • guys thank you for your comments.

    I noticed that I cannot cast io.Reader to io.Seeker. for some reason I thought that io.Reader got some sort of rewind method that I missed but this is not the case.

    so what I did is read the data to a byte array and created new Reader object whenever I need it:

    byteArray, err := ioutil.ReadAll(imgFile.File)
    if err != nil {
        return err
    }
    
    switch imgFile.ContentType {
    case "image/png":
        if myImage, err = png.Decode(bytes.NewReader(byteArray)); err != nil {
            return err
        }
        break
    case "image/jpeg":
        if myImage, err = jpeg.Decode(bytes.NewReader(byteArray)); err != nil {
            return err
        }
        break
    case "image/webp":
        if myImage, err = webpbin.Decode(bytes.NewReader(byteArray)); err != nil {
            return err
        }
        break
    
    default:
        return errors.Errorf("unknown image mimetype %v", imgFile.ContentType)
    }
    metaData, err := exif.Decode(bytes.NewReader(byteArray))