Skip to content

Instantly share code, notes, and snippets.

@rayrutjes
Created December 12, 2015 13:36
Show Gist options
  • Star 42 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save rayrutjes/db9b9ea8e02255d62ce2 to your computer and use it in GitHub Desktop.
Save rayrutjes/db9b9ea8e02255d62ce2 to your computer and use it in GitHub Desktop.
golang detect content type of a file
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// Only the first 512 bytes are used to sniff the content type.
buffer := make([]byte, 512)
_, err = file.Read(buffer)
if err != nil {
return err
}
// Reset the read pointer if necessary.
file.Seek(0, 0)
// Always returns a valid content-type and "application/octet-stream" if no others seemed to match.
contentType := http.DetectContentType(buffer)
@mogaika
Copy link

mogaika commented Mar 29, 2016

// function still work, if size of buffer < 512
n, err = file.Read(buffer)
if err != nil && err != io.EOF {
    return err
}
contentType := http.DetectContentType(buffer[:n])

@berkant
Copy link

berkant commented Jun 2, 2019

@mogaika This is no longer needed. DetectContentType handles it itself.

@mogaika
Copy link

mogaika commented Jun 4, 2019

@0xbkt Handles what? err != nil { return err } will drop detection for files with size < 512 bytes

@bst27
Copy link

bst27 commented Aug 15, 2020

Yes, @mogaika is right. I also found another problem: Text files with less than 512 bytes are detected as application/octet-stream. The problem seems to be that buffer is filled with zero values up top a length of 512 bytes. If you slice the array to the size actually returned from Read() before passing it to http.DetectContentType it correctly returns text/plain; charset=utf8:

func Get(seeker io.ReadSeeker) (string, error) {
	// At most the first 512 bytes of data are used:
	// https://golang.org/src/net/http/sniff.go?s=646:688#L11
	buff := make([]byte, 512)

	_, err := seeker.Seek(0, io.SeekStart)
	if err != nil {
		return "", err
	}

	bytesRead, err := seeker.Read(buff)
	if err != nil && err != io.EOF {
		return "", err
	}

	// Slice to remove fill-up zero values which cause a wrong content type detection in the next step
	buff = buff[:bytesRead]

	return http.DetectContentType(buff), nil
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment