9
9
"fmt"
10
10
"io"
11
11
"net/http"
12
+ "path/filepath"
12
13
"regexp"
13
14
"slices"
14
15
"strings"
@@ -26,6 +27,14 @@ const (
26
27
MimeTypeApplicationOctetStream = "application/octet-stream"
27
28
)
28
29
30
+ // list of supported CAD 3D file extensions
31
+ var supportedCAD3DFileExtensions = []string {
32
+ ".3dm" , ".3ds" , ".3mf" , ".amf" , ".bim" , ".brep" ,
33
+ ".dae" , ".fbx" , ".fcstd" , ".glb" , ".gltf" ,
34
+ ".ifc" , ".igs" , ".iges" , ".stp" , ".step" ,
35
+ ".stl" , ".obj" , ".off" , ".ply" , ".wrl" ,
36
+ }
37
+
29
38
var (
30
39
svgComment = regexp .MustCompile (`(?s)<!--.*?-->` )
31
40
svgTagRegex = regexp .MustCompile (`(?si)\A\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b` )
35
44
// SniffedType contains information about a blobs type.
36
45
type SniffedType struct {
37
46
contentType string
47
+ fileName string
38
48
}
39
49
40
50
// IsText etects if content format is plain text.
@@ -67,6 +77,20 @@ func (ct SniffedType) IsAudio() bool {
67
77
return strings .Contains (ct .contentType , "audio/" )
68
78
}
69
79
80
+ // Is3DFile detects if data is a 3D model file format
81
+ func (ct SniffedType ) Is3DFile () bool {
82
+ if ct .fileName == "" {
83
+ return false
84
+ }
85
+ ext := strings .ToLower (filepath .Ext (ct .fileName ))
86
+ for _ , supportedExt := range supportedCAD3DFileExtensions {
87
+ if ext == supportedExt {
88
+ return true
89
+ }
90
+ }
91
+ return false
92
+ }
93
+
70
94
// IsRepresentableAsText returns true if file content can be represented as
71
95
// plain text or is empty.
72
96
func (ct SniffedType ) IsRepresentableAsText () bool {
@@ -75,7 +99,7 @@ func (ct SniffedType) IsRepresentableAsText() bool {
75
99
76
100
// IsBrowsableBinaryType returns whether a non-text type can be displayed in a browser
77
101
func (ct SniffedType ) IsBrowsableBinaryType () bool {
78
- return ct .IsImage () || ct .IsSvgImage () || ct .IsPDF () || ct .IsVideo () || ct .IsAudio ()
102
+ return ct .IsImage () || ct .IsSvgImage () || ct .IsPDF () || ct .IsVideo () || ct .IsAudio () || ct . Is3DFile ()
79
103
}
80
104
81
105
// GetMimeType returns the mime type
@@ -105,8 +129,13 @@ func detectFileTypeBox(data []byte) (brands []string, found bool) {
105
129
106
130
// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty.
107
131
func DetectContentType (data []byte ) SniffedType {
132
+ return DetectContentTypeWithFilename (data , "" )
133
+ }
134
+
135
+ // DetectContentTypeWithFilename extends http.DetectContentType with more content types and uses filename for additional detection. Defaults to text/unknown if input is empty.
136
+ func DetectContentTypeWithFilename (data []byte , fileName string ) SniffedType {
108
137
if len (data ) == 0 {
109
- return SniffedType {"text/unknown" }
138
+ return SniffedType {"text/unknown" , fileName }
110
139
}
111
140
112
141
ct := http .DetectContentType (data )
@@ -153,17 +182,22 @@ func DetectContentType(data []byte) SniffedType {
153
182
ct = "audio/ogg" // for most cases, it is used as an audio container
154
183
}
155
184
}
156
- return SniffedType {ct }
185
+ return SniffedType {ct , fileName }
157
186
}
158
187
159
188
// DetectContentTypeFromReader guesses the content type contained in the reader.
160
189
func DetectContentTypeFromReader (r io.Reader ) (SniffedType , error ) {
190
+ return DetectContentTypeFromReaderWithFilename (r , "" )
191
+ }
192
+
193
+ // DetectContentTypeFromReaderWithFilename guesses the content type contained in the reader with given filename.
194
+ func DetectContentTypeFromReaderWithFilename (r io.Reader , fileName string ) (SniffedType , error ) {
161
195
buf := make ([]byte , sniffLen )
162
196
n , err := util .ReadAtMost (r , buf )
163
197
if err != nil {
164
198
return SniffedType {}, fmt .Errorf ("DetectContentTypeFromReader io error: %w" , err )
165
199
}
166
200
buf = buf [:n ]
167
201
168
- return DetectContentType (buf ), nil
202
+ return DetectContentTypeWithFilename (buf , fileName ), nil
169
203
}
0 commit comments