hartfordfive
hartfordfive

Reputation: 1275

Calculating SHA1 hash on MultipartForm file upload in Go

I'm trying to calculate the sha1 hash of an uploaded file but so far I'm at a dead end. The sample code is as follows:

err := req.ParseMultipartForm(200000)
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

m := req.MultipartForm
files := m.File["Filedata"]

for i, _ := range files {
        file, err := files[i].Open()
        defer file.Close()
    fh = getFileHash(file)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        break
    }

    dst, err := os.Create(baseDir+fh+".jpg")
    defer dst.Close()
    if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            break
    }

    if _, err := io.Copy(dst, file); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                break
    }
}

And the content of the getFileHash function is as follows:

func getFileHash (f *os.File) string {
     fstat, err := f.Stat()
     if err != nil {
    panic(err)
     }
     fSize := fstat.Size()
     buf := make([]byte, fSize)
     hash := sha1.New()
     n, err := f.Read(buf)
     if _, err := io.WriteString(hash, string(buf[:n])); err != nil {
           panic(err)
     }
     return string(hash.Sum(nil))
}

I'm getting the following error although I'm not sure how to get around this:

cannot use file (type multipart.File) as type *os.File in function argument: need type assertion

If I test by using a sample hash as the value of "fh", it works just fine.

I know I could simply save the file, calculate the hash, and then move the file although that's extra steps I'd rather not take if possible. I'd appreciate any help you could provide me with.

Thanks!

Upvotes: 2

Views: 2023

Answers (2)

Dustin
Dustin

Reputation: 91030

You can stream the file to disk and a hash function at the same time. Here's a rough example:

Note: It does have one caveat: The error is not checked on the file being written. This can matter tremendously depending on your filesystem. You might consider something like errutil in production code.

play link

package main

import (
    "crypto/sha1"
    "fmt"
    "io"
    "os"
    "strings"
)

func shaAndSave(src io.Reader) ([]byte, error) {
    h := sha1.New()
    dest, err := os.Create("tmpfile.txt")
    if err != nil {
        return nil, err
    }
    defer dest.Close()
    t := io.TeeReader(src, h)
    _, err = io.Copy(dest, t)
    if err != nil {
        return nil, err
    }

    return h.Sum(nil), nil
}

func main() {
    src := strings.NewReader("pretend this was a file")

    hashBytes, err := shaAndSave(src)
    fmt.Printf("Returned %x and %v", hashBytes, err)
}

Upvotes: 1

kwolfe
kwolfe

Reputation: 1683

From http://golang.org/pkg/mime/multipart/#File

File is an interface to access the file part of a multipart message. Its contents may be either stored in memory or on disk. If stored on disk, the File's underlying concrete type will be an *os.File.

So you are indeed not passing an *os.File type, since it is in memory. Try accepting multipart.File as a type.

Upvotes: 0

Related Questions