You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
83 lines
1.9 KiB
83 lines
1.9 KiB
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
|
|
}
|