mime.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package gold
  2. import (
  3. "errors"
  4. "fmt"
  5. "mime"
  6. "path/filepath"
  7. "github.com/gabriel-vasile/mimetype"
  8. crdf "github.com/presbrey/goraptor"
  9. "regexp"
  10. "sync"
  11. )
  12. var mimeParser = map[string]string{
  13. "application/ld+json": "jsonld",
  14. "application/json": "internal",
  15. "application/sparql-update": "internal",
  16. }
  17. var mimeSerializer = map[string]string{
  18. "application/ld+json": "internal",
  19. "text/html": "internal",
  20. }
  21. var mimeRdfExt = map[string]string{
  22. ".ttl": "text/turtle",
  23. ".n3": "text/n3",
  24. ".rdf": "application/rdf+xml",
  25. ".jsonld": "application/ld+json",
  26. }
  27. var rdfExtensions = []string{
  28. ".ttl",
  29. ".n3",
  30. ".rdf",
  31. ".jsonld",
  32. }
  33. var (
  34. serializerMimes = []string{}
  35. validMimeType = regexp.MustCompile(`^\w+/\w+$`)
  36. mutex = &sync.Mutex{}
  37. )
  38. func init() {
  39. // add missing extensions
  40. for k, v := range mimeRdfExt {
  41. mime.AddExtensionType(k, v)
  42. }
  43. for _, syntax := range crdf.ParserSyntax {
  44. switch syntax.MimeType {
  45. case "", "text/html":
  46. continue
  47. }
  48. mimeParser[syntax.MimeType] = syntax.Name
  49. }
  50. mimeParser["text/n3"] = mimeParser["text/turtle"]
  51. for name, syntax := range crdf.SerializerSyntax {
  52. switch name {
  53. case "json-triples":
  54. // only activate: json
  55. continue
  56. case "rdfxml-xmp", "rdfxml":
  57. // only activate: rdfxml-abbrev
  58. continue
  59. }
  60. mimeSerializer[syntax.MimeType] = syntax.Name
  61. }
  62. for mime := range mimeSerializer {
  63. switch mime {
  64. case "application/xhtml+xml":
  65. continue
  66. }
  67. serializerMimes = append(serializerMimes, mime)
  68. }
  69. }
  70. func GuessMimeType(path string) (string, error) {
  71. mimeType := "text/plain"
  72. guessedType, _, err := mimetype.DetectFile(path)
  73. if err != nil {
  74. fmt.Printf("Unknown mimeType from file: %s ==> %s", path, err)
  75. return mimeType, err
  76. }
  77. if guessedType != "" && validMimeType.MatchString(guessedType) {
  78. mimeType = guessedType
  79. }
  80. return mimeType, nil
  81. }
  82. func LookupExt(ctype string) string {
  83. for k, v := range mimeRdfExt {
  84. if v == ctype {
  85. return k
  86. }
  87. }
  88. return ""
  89. }
  90. func LookUpCtype(ext string) string {
  91. return mimeRdfExt[ext]
  92. }
  93. func AddRDFExtension(ext string) {
  94. rdfExtensions = append(rdfExtensions, ext)
  95. }
  96. func IsRdfExtension(ext string) bool {
  97. for _, v := range rdfExtensions {
  98. if v == ext {
  99. return true
  100. }
  101. }
  102. return false
  103. }
  104. func MimeLookup(path string) (string, string, bool) {
  105. var mimeType string
  106. maybeRDF := false
  107. ext := filepath.Ext(path)
  108. if len(ext) > 0 {
  109. if IsRdfExtension(ext) {
  110. maybeRDF = true
  111. mimeType = LookUpCtype(ext)
  112. } else {
  113. mimeType = mime.TypeByExtension(ext)
  114. if len(mimeType) > 0 {
  115. if len(LookupExt(ext)) > 0 {
  116. maybeRDF = true
  117. }
  118. }
  119. }
  120. }
  121. return mimeType, ext, maybeRDF
  122. }
  123. // MapPathToExtension returns the path with the proper extension that matches the given content type,
  124. // even if the resource (path) contains a different extension
  125. // Only works with Go 1.5+
  126. //@@TODO should switch to a more comprehensive list of mime-to-ext (instead of using go's internal list)
  127. func MapPathToExtension(path string, ctype string) (string, error) {
  128. if len(path) == 0 {
  129. return "", errors.New("MapPathToExt -- missing path or ctype value")
  130. }
  131. if path[len(path)-1:] == "/" {
  132. return path, nil
  133. }
  134. fileCType, ext, _ := MimeLookup(path)
  135. if len(fileCType) > 0 {
  136. fileCType, _, _ = mime.ParseMediaType(fileCType)
  137. if len(ctype) > 0 {
  138. if fileCType != ctype {
  139. // append the extension corresponding to Content-Type header
  140. newExt, err := mime.ExtensionsByType(ctype)
  141. if err != nil {
  142. return "", err
  143. }
  144. if len(newExt) > 0 {
  145. ext = newExt[0]
  146. }
  147. path += "$" + ext
  148. }
  149. }
  150. } else {
  151. if len(ext) > 0 {
  152. if len(ctype) > 0 {
  153. newExt, err := mime.ExtensionsByType(ctype)
  154. if err != nil {
  155. return "", err
  156. }
  157. if len(newExt) > 0 {
  158. match := false
  159. for _, e := range newExt {
  160. if e == ext {
  161. match = true
  162. break
  163. }
  164. }
  165. if !match {
  166. // could not find matching extension
  167. if !IsRdfExtension(newExt[0]) {
  168. path += "$" + newExt[0]
  169. }
  170. }
  171. }
  172. }
  173. } else {
  174. // !fileCtype, !ext, ctype
  175. if len(ctype) > 0 {
  176. // maybe it's an RDF resource
  177. if ext = LookupExt(ctype); len(ext) > 0 {
  178. path += ext
  179. } else {
  180. newExt, err := mime.ExtensionsByType(ctype)
  181. if err != nil {
  182. return "", err
  183. }
  184. if len(newExt) > 0 {
  185. path += newExt[0]
  186. }
  187. }
  188. } else {
  189. return "", errors.New("Cannot infer mime type from from empty file")
  190. }
  191. }
  192. }
  193. return path, nil
  194. }