2020-09-26 11:22:21 +08:00
|
|
|
|
package nodes
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
2020-11-21 22:29:57 +08:00
|
|
|
|
"github.com/cespare/xxhash"
|
2020-09-26 11:22:21 +08:00
|
|
|
|
"github.com/iwind/TeaGo/Tea"
|
|
|
|
|
|
"github.com/iwind/TeaGo/logs"
|
|
|
|
|
|
"io"
|
2021-01-26 18:42:46 +08:00
|
|
|
|
"io/fs"
|
2020-09-26 11:22:21 +08:00
|
|
|
|
"mime"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"net/url"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 文本mime-type列表
|
|
|
|
|
|
var textMimeMap = map[string]bool{
|
|
|
|
|
|
"application/atom+xml": true,
|
|
|
|
|
|
"application/javascript": true,
|
|
|
|
|
|
"application/x-javascript": true,
|
|
|
|
|
|
"application/json": true,
|
|
|
|
|
|
"application/rss+xml": true,
|
|
|
|
|
|
"application/x-web-app-manifest+json": true,
|
|
|
|
|
|
"application/xhtml+xml": true,
|
|
|
|
|
|
"application/xml": true,
|
|
|
|
|
|
"image/svg+xml": true,
|
|
|
|
|
|
"text/css": true,
|
|
|
|
|
|
"text/plain": true,
|
|
|
|
|
|
"text/javascript": true,
|
|
|
|
|
|
"text/xml": true,
|
|
|
|
|
|
"text/html": true,
|
|
|
|
|
|
"text/xhtml": true,
|
|
|
|
|
|
"text/sgml": true,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调用本地静态资源
|
|
|
|
|
|
// 如果返回true,则终止请求
|
|
|
|
|
|
func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|
|
|
|
|
if this.web.Root == nil || !this.web.Root.IsOn {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(this.uri) == 0 {
|
|
|
|
|
|
this.write404()
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rootDir := this.web.Root.Dir
|
2020-09-26 19:54:26 +08:00
|
|
|
|
if this.web.Root.HasVariables() {
|
|
|
|
|
|
rootDir = this.Format(rootDir)
|
|
|
|
|
|
}
|
2020-09-26 11:22:21 +08:00
|
|
|
|
if !filepath.IsAbs(rootDir) {
|
|
|
|
|
|
rootDir = Tea.Root + Tea.DS + rootDir
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
requestPath := this.uri
|
|
|
|
|
|
|
|
|
|
|
|
questionMarkIndex := strings.Index(this.uri, "?")
|
|
|
|
|
|
if questionMarkIndex > -1 {
|
|
|
|
|
|
requestPath = this.uri[:questionMarkIndex]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 去掉其中的奇怪的路径
|
|
|
|
|
|
requestPath = strings.Replace(requestPath, "..\\", "", -1)
|
|
|
|
|
|
|
|
|
|
|
|
// 进行URL Decode
|
|
|
|
|
|
if this.web.Root.DecodePath {
|
|
|
|
|
|
p, err := url.QueryUnescape(requestPath)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
requestPath = p
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 去掉前缀
|
|
|
|
|
|
stripPrefix := this.web.Root.StripPrefix
|
|
|
|
|
|
if len(stripPrefix) > 0 {
|
|
|
|
|
|
if stripPrefix[0] != '/' {
|
|
|
|
|
|
stripPrefix = "/" + stripPrefix
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
requestPath = strings.TrimPrefix(requestPath, stripPrefix)
|
|
|
|
|
|
if len(requestPath) == 0 || requestPath[0] != '/' {
|
|
|
|
|
|
requestPath = "/" + requestPath
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
filename := strings.Replace(requestPath, "/", Tea.DS, -1)
|
|
|
|
|
|
filePath := ""
|
|
|
|
|
|
if len(filename) > 0 && filename[0:1] == Tea.DS {
|
|
|
|
|
|
filePath = rootDir + filename
|
|
|
|
|
|
} else {
|
|
|
|
|
|
filePath = rootDir + Tea.DS + filename
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.filePath = filePath // 用来记录日志
|
|
|
|
|
|
|
|
|
|
|
|
stat, err := os.Stat(filePath)
|
|
|
|
|
|
if err != nil {
|
2021-01-26 18:42:46 +08:00
|
|
|
|
_, isPathError := err.(*fs.PathError)
|
|
|
|
|
|
if os.IsNotExist(err) || isPathError {
|
2020-09-26 11:22:21 +08:00
|
|
|
|
if this.web.Root.IsBreak {
|
|
|
|
|
|
this.write404()
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
} else {
|
2021-09-01 08:48:03 +08:00
|
|
|
|
this.write50x(err, http.StatusInternalServerError)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if stat.IsDir() {
|
|
|
|
|
|
indexFile, indexStat := this.findIndexFile(filePath)
|
|
|
|
|
|
if len(indexFile) > 0 {
|
|
|
|
|
|
filePath += Tea.DS + indexFile
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if this.web.Root.IsBreak {
|
|
|
|
|
|
this.write404()
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.filePath = filePath
|
|
|
|
|
|
|
|
|
|
|
|
// stat again
|
|
|
|
|
|
if indexStat == nil {
|
|
|
|
|
|
stat, err = os.Stat(filePath)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
|
if this.web.Root.IsBreak {
|
|
|
|
|
|
this.write404()
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
} else {
|
2021-09-01 08:48:03 +08:00
|
|
|
|
this.write50x(err, http.StatusInternalServerError)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
stat = indexStat
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 响应header
|
|
|
|
|
|
respHeader := this.writer.Header()
|
|
|
|
|
|
|
|
|
|
|
|
// mime type
|
2021-01-10 22:35:34 +08:00
|
|
|
|
contentType := ""
|
2020-09-26 19:54:26 +08:00
|
|
|
|
if this.web.ResponseHeaderPolicy == nil || !this.web.ResponseHeaderPolicy.IsOn || !this.web.ResponseHeaderPolicy.ContainsHeader("CONTENT-TYPE") {
|
2020-09-29 17:21:46 +08:00
|
|
|
|
ext := filepath.Ext(filePath)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
if len(ext) > 0 {
|
|
|
|
|
|
mimeType := mime.TypeByExtension(ext)
|
|
|
|
|
|
if len(mimeType) > 0 {
|
2020-09-26 19:54:26 +08:00
|
|
|
|
semicolonIndex := strings.Index(mimeType, ";")
|
|
|
|
|
|
mimeTypeKey := mimeType
|
|
|
|
|
|
if semicolonIndex > 0 {
|
|
|
|
|
|
mimeTypeKey = mimeType[:semicolonIndex]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if _, found := textMimeMap[mimeTypeKey]; found {
|
2020-09-26 11:22:21 +08:00
|
|
|
|
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
|
|
|
|
|
|
charset := this.web.Charset.Charset
|
2020-09-26 19:54:26 +08:00
|
|
|
|
if this.web.Charset.IsUpper {
|
|
|
|
|
|
charset = strings.ToUpper(charset)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
}
|
2021-01-10 22:35:34 +08:00
|
|
|
|
contentType = mimeTypeKey + "; charset=" + charset
|
2020-09-26 19:54:26 +08:00
|
|
|
|
respHeader.Set("Content-Type", mimeTypeKey+"; charset="+charset)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
} else {
|
2021-01-10 22:35:34 +08:00
|
|
|
|
contentType = mimeType
|
2020-09-26 11:22:21 +08:00
|
|
|
|
respHeader.Set("Content-Type", mimeType)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2021-01-10 22:35:34 +08:00
|
|
|
|
contentType = mimeType
|
2020-09-26 11:22:21 +08:00
|
|
|
|
respHeader.Set("Content-Type", mimeType)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// length
|
|
|
|
|
|
fileSize := stat.Size()
|
|
|
|
|
|
|
|
|
|
|
|
// 支持 Last-Modified
|
|
|
|
|
|
modifiedTime := stat.ModTime().Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
|
|
|
|
|
if len(respHeader.Get("Last-Modified")) == 0 {
|
|
|
|
|
|
respHeader.Set("Last-Modified", modifiedTime)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 支持 ETag
|
2021-01-10 22:35:34 +08:00
|
|
|
|
eTag := "\"e" + fmt.Sprintf("%0x", xxhash.Sum64String(filename+strconv.FormatInt(stat.ModTime().UnixNano(), 10)+strconv.FormatInt(stat.Size(), 10))) + "\""
|
2020-09-26 11:22:21 +08:00
|
|
|
|
if len(respHeader.Get("ETag")) == 0 {
|
|
|
|
|
|
respHeader.Set("ETag", eTag)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 支持 If-None-Match
|
|
|
|
|
|
if this.requestHeader("If-None-Match") == eTag {
|
|
|
|
|
|
// 自定义Header
|
|
|
|
|
|
this.processResponseHeaders(http.StatusNotModified)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusNotModified)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 支持 If-Modified-Since
|
|
|
|
|
|
if this.requestHeader("If-Modified-Since") == modifiedTime {
|
|
|
|
|
|
// 自定义Header
|
|
|
|
|
|
this.processResponseHeaders(http.StatusNotModified)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusNotModified)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-10 22:35:34 +08:00
|
|
|
|
// 支持Range
|
|
|
|
|
|
respHeader.Set("Accept-Ranges", "bytes")
|
|
|
|
|
|
ifRangeHeaders, ok := this.RawReq.Header["If-Range"]
|
|
|
|
|
|
supportRange := true
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
supportRange = false
|
|
|
|
|
|
for _, v := range ifRangeHeaders {
|
|
|
|
|
|
if v == eTag || v == modifiedTime {
|
|
|
|
|
|
supportRange = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-01-13 12:52:38 +08:00
|
|
|
|
if !supportRange {
|
|
|
|
|
|
respHeader.Del("Accept-Ranges")
|
|
|
|
|
|
}
|
2021-01-10 22:35:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 支持Range
|
2021-01-13 12:52:38 +08:00
|
|
|
|
rangeSet := [][]int64{}
|
2021-01-10 22:35:34 +08:00
|
|
|
|
if supportRange {
|
|
|
|
|
|
contentRange := this.RawReq.Header.Get("Range")
|
|
|
|
|
|
if len(contentRange) > 0 {
|
2021-01-13 12:52:38 +08:00
|
|
|
|
if fileSize == 0 {
|
|
|
|
|
|
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-10 22:35:34 +08:00
|
|
|
|
set, ok := httpRequestParseContentRange(contentRange)
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(set) > 0 {
|
|
|
|
|
|
rangeSet = set
|
|
|
|
|
|
for _, arr := range rangeSet {
|
|
|
|
|
|
if arr[0] == -1 {
|
2021-01-13 12:52:38 +08:00
|
|
|
|
arr[0] = fileSize + arr[1]
|
|
|
|
|
|
arr[1] = fileSize - 1
|
2021-01-10 22:35:34 +08:00
|
|
|
|
|
|
|
|
|
|
if arr[0] < 0 {
|
|
|
|
|
|
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-01-13 12:52:38 +08:00
|
|
|
|
if arr[1] > 0 {
|
|
|
|
|
|
arr[1] = fileSize - 1
|
|
|
|
|
|
}
|
|
|
|
|
|
if arr[1] < 0 {
|
|
|
|
|
|
arr[1] = fileSize - 1
|
|
|
|
|
|
}
|
|
|
|
|
|
if arr[0] > arr[1] {
|
|
|
|
|
|
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
2021-01-10 22:35:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
respHeader.Set("Content-Length", strconv.FormatInt(fileSize, 10))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
respHeader.Set("Content-Length", strconv.FormatInt(fileSize, 10))
|
|
|
|
|
|
}
|
2020-09-26 11:22:21 +08:00
|
|
|
|
|
|
|
|
|
|
reader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
|
|
|
|
|
|
if err != nil {
|
2021-09-01 08:48:03 +08:00
|
|
|
|
this.write50x(err, http.StatusInternalServerError)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-10 22:35:34 +08:00
|
|
|
|
// 自定义Header
|
|
|
|
|
|
this.processResponseHeaders(http.StatusOK)
|
|
|
|
|
|
|
|
|
|
|
|
// 在Range请求中不能缓存
|
|
|
|
|
|
if len(rangeSet) > 0 {
|
|
|
|
|
|
this.cacheRef = nil // 不支持缓存
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-23 14:29:56 +08:00
|
|
|
|
this.writer.Prepare(fileSize, http.StatusOK)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
|
|
|
|
|
|
pool := this.bytePool(fileSize)
|
|
|
|
|
|
buf := pool.Get()
|
2021-01-10 22:35:34 +08:00
|
|
|
|
defer func() {
|
|
|
|
|
|
_ = reader.Close()
|
|
|
|
|
|
pool.Put(buf)
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
if len(rangeSet) == 1 {
|
2021-01-13 12:52:38 +08:00
|
|
|
|
respHeader.Set("Content-Range", "bytes "+strconv.FormatInt(rangeSet[0][0], 10)+"-"+strconv.FormatInt(rangeSet[0][1], 10)+"/"+strconv.FormatInt(fileSize, 10))
|
2021-01-10 22:35:34 +08:00
|
|
|
|
this.writer.WriteHeader(http.StatusPartialContent)
|
|
|
|
|
|
|
|
|
|
|
|
ok, err := httpRequestReadRange(reader, buf, rangeSet[0][0], rangeSet[0][1], func(buf []byte, n int) error {
|
|
|
|
|
|
_, err := this.writer.Write(buf[:n])
|
|
|
|
|
|
return err
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if len(rangeSet) > 1 {
|
|
|
|
|
|
boundary := httpRequestGenBoundary()
|
|
|
|
|
|
respHeader.Set("Content-Type", "multipart/byteranges; boundary="+boundary)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
|
2021-01-10 22:35:34 +08:00
|
|
|
|
this.writer.WriteHeader(http.StatusPartialContent)
|
2020-09-26 11:22:21 +08:00
|
|
|
|
|
2021-01-10 22:35:34 +08:00
|
|
|
|
for index, set := range rangeSet {
|
|
|
|
|
|
if index == 0 {
|
|
|
|
|
|
_, err = this.writer.WriteString("--" + boundary + "\r\n")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_, err = this.writer.WriteString("\r\n--" + boundary + "\r\n")
|
|
|
|
|
|
}
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-13 12:52:38 +08:00
|
|
|
|
_, err = this.writer.WriteString("Content-Range: " + "bytes " + strconv.FormatInt(set[0], 10) + "-" + strconv.FormatInt(set[1], 10) + "/" + strconv.FormatInt(fileSize, 10) + "\r\n")
|
2021-01-10 22:35:34 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(contentType) > 0 {
|
|
|
|
|
|
_, err = this.writer.WriteString("Content-Type: " + contentType + "\r\n\r\n")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ok, err := httpRequestReadRange(reader, buf, set[0], set[1], func(buf []byte, n int) error {
|
|
|
|
|
|
_, err := this.writer.Write(buf[:n])
|
|
|
|
|
|
return err
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_, err = this.writer.WriteString("\r\n--" + boundary + "--\r\n")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_, err = io.CopyBuffer(this.writer, reader, buf)
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
2020-09-26 11:22:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// 设置成功
|
|
|
|
|
|
this.writer.SetOk()
|
|
|
|
|
|
|
2020-09-26 11:22:21 +08:00
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找首页文件
|
|
|
|
|
|
func (this *HTTPRequest) findIndexFile(dir string) (filename string, stat os.FileInfo) {
|
|
|
|
|
|
if this.web.Root == nil || !this.web.Root.IsOn {
|
|
|
|
|
|
return "", nil
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(this.web.Root.Indexes) == 0 {
|
|
|
|
|
|
return "", nil
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, index := range this.web.Root.Indexes {
|
|
|
|
|
|
if len(index) == 0 {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 模糊查找
|
|
|
|
|
|
if strings.Contains(index, "*") {
|
|
|
|
|
|
indexFiles, err := filepath.Glob(dir + Tea.DS + index)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logs.Error(err)
|
|
|
|
|
|
this.addError(err)
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(indexFiles) > 0 {
|
|
|
|
|
|
return filepath.Base(indexFiles[0]), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 精确查找
|
|
|
|
|
|
filePath := dir + Tea.DS + index
|
|
|
|
|
|
stat, err := os.Stat(filePath)
|
|
|
|
|
|
if err != nil || !stat.Mode().IsRegular() {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
return index, stat
|
|
|
|
|
|
}
|
|
|
|
|
|
return "", nil
|
|
|
|
|
|
}
|