Writer struct allowing io.Copy or io.TeeReader from a reader
History of this PR:
I was looking to write an io.Reader (a file incoming through http multipart) into a file while extracting the MD5 checksum (using crypto/md5)and detect the MIME type. I came across with this library but I realized I needed to create an io.Writer struct to wrap this library in order to use it along with io.TeeReader to perform the three actions in one step.
Work done:
I basically created a struct that wraps a buffer which reads the first 8192 bytes and discards the rest by implemented the io.Writer. I also added a method that passes the internal buffer into the Match function.
Also I added a simple test which could be improved.
Things I don't like:
- It seems that the
Writemethod (which makes the struct coply withio.Writerinterface) could be improved, at least it seems a bit complicated. - The name of the struct could be better
Anyway, I wanted to know what do you think of this patch. Maybe it is not necessary, maybe is not even a good patch. Here is how I ended up using it.
// FileMetadata is a bit masking value to enumerate options to extract metadata from writers
type FileMetadata uint32
const (
// Checksum extracts the file checksum from the reader
Checksum FileMetadata = 1 << iota
// MIME extracts the file MIME from the reader
MIME
)
// WriteInfo modes all the information that can be extracted when writing a Writer
type WriteInfo struct {
Written int64
Checksum []byte
MimeType string
}
// WriteWithMetadata writes a reader to a writer and extract the required metadata
func WriteWithMetadata(writer io.Writer, reader io.Reader, meta FileMetadata) (WriteInfo, error) {
var h hash.Hash
var t types.Type
var mw *filetype.MatcherWriter
wi := WriteInfo{}
currentReader := reader
if meta&Checksum != 0 {
h = md5.New()
teeReader := io.TeeReader(currentReader, h)
currentReader = teeReader
}
if meta&MIME != 0 {
mw = filetype.NewMatcherWriter()
teeReader := io.TeeReader(currentReader, mw)
currentReader = teeReader
}
written, err := io.Copy(writer, currentReader)
wi.Written = written
if err != nil {
return wi, err
}
if meta&Checksum != 0 {
wi.Checksum = h.Sum(nil)
}
if meta&MIME != 0 {
// if error it will be returned on the last return
t, err = mw.Match()
wi.MimeType = t.MIME.Value
}
return wi, err
}
Hey, thanks for this library. It's really good.