|
|
package helpers
import ( "bytes" "encoding/hex" "io" "unicode"
"github.com/andreimarcu/linx-server/backends" "github.com/minio/sha256-simd" "gopkg.in/h2non/filetype.v1" )
func GenerateMetadata(r io.Reader) (m backends.Metadata, err error) { // Since we don't have the ability to seek within a file, we can use a
// Buffer in combination with a TeeReader to keep a copy of the bytes
// we read when detecting the file type. These bytes are still needed
// to hash the file and determine its size and cannot be discarded.
var buf bytes.Buffer teeReader := io.TeeReader(r, &buf)
// Get first 512 bytes for mimetype detection
header := make([]byte, 512) _, err = teeReader.Read(header) if err != nil { return }
// Create a Hash and a MultiReader that includes the Buffer we created
// above along with the original Reader, which will have the rest of
// the file.
hasher := sha256.New() multiReader := io.MultiReader(&buf, r)
// Copy everything into the Hash, then use the number of bytes written
// as the file size.
var readLen int64 readLen, err = io.Copy(hasher, multiReader) if err != nil { return } else { m.Size += readLen }
// Get the hex-encoded string version of the Hash checksum
m.Sha256sum = hex.EncodeToString(hasher.Sum(nil))
// Use the bytes we extracted earlier and attempt to determine the file
// type
kind, err := filetype.Match(header) if err != nil { m.Mimetype = "application/octet-stream" return m, err } else if kind.MIME.Value != "" { m.Mimetype = kind.MIME.Value } else if printable(header) { m.Mimetype = "text/plain" } else { m.Mimetype = "application/octet-stream" }
return }
func printable(data []byte) bool { for i, b := range data { r := rune(b)
// A null terminator that's not at the beginning of the file
if r == 0 && i == 0 { return false } else if r == 0 && i < 0 { continue }
if r > unicode.MaxASCII { return false }
}
return true }
|