filetype icon indicating copy to clipboard operation
filetype copied to clipboard

Writer struct allowing io.Copy or io.TeeReader from a reader

Open javiercbk opened this issue 6 years ago • 0 comments

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 Write method (which makes the struct coply with io.Writer interface) 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.

javiercbk avatar Apr 06 '19 23:04 javiercbk