161 lines
4.2 KiB
Go
Executable File
161 lines
4.2 KiB
Go
Executable File
package main
|
||
|
||
import (
|
||
"io"
|
||
"net/http"
|
||
"net/url"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
)
|
||
|
||
// ListFiles function: Traverse all files in the specified directory and return a slice of the file path
|
||
func ListFiles(dir string) ([]string, error) {
|
||
var files []string
|
||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if !info.IsDir() {
|
||
files = append(files, path)
|
||
}
|
||
return nil
|
||
})
|
||
return files, err
|
||
}
|
||
|
||
// Get Content function: Read the content of a specified file and return it
|
||
func GetFileContent(filePath string) ([]byte, error) {
|
||
file, err := os.Open(filePath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer file.Close()
|
||
|
||
// Get File Size
|
||
fileInfo, err := file.Stat()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
fileSize := fileInfo.Size()
|
||
|
||
// Read File Content
|
||
fileContent := make([]byte, fileSize)
|
||
_, err = file.Read(fileContent)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return fileContent, nil
|
||
}
|
||
|
||
// fileHandler function: Handle file requests
|
||
func fileHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Server", "MeowMusicEmbeddedServer")
|
||
filePath := r.URL.Path
|
||
|
||
// 提前URL解码
|
||
decodedPath, decodeErr := url.QueryUnescape(filePath)
|
||
if decodeErr == nil {
|
||
// 兼容历史数据:将+替换为空格(仅当解码成功时)
|
||
decodedPath = strings.ReplaceAll(decodedPath, "+", " ")
|
||
filePath = decodedPath // 后续统一使用解码后路径
|
||
}
|
||
|
||
// 处理 /url/ 远程请求(保持不变)
|
||
if strings.HasPrefix(filePath, "/url/") {
|
||
// Extract the URL after "/url/"
|
||
urlPath := filePath[len("/url/"):]
|
||
// Decode the URL path in case it's URL encoded
|
||
decodedURL, err := url.QueryUnescape(urlPath)
|
||
if err != nil {
|
||
NotFoundHandler(w, r)
|
||
return
|
||
}
|
||
// Determine the protocol based on the URL path
|
||
var protocol string
|
||
if strings.HasPrefix(decodedURL, "http/") {
|
||
protocol = "http://"
|
||
} else if strings.HasPrefix(decodedURL, "https/") {
|
||
protocol = "https://"
|
||
} else {
|
||
NotFoundHandler(w, r)
|
||
return
|
||
}
|
||
// Remove the protocol part from the decoded URL
|
||
decodedURL = strings.TrimPrefix(decodedURL, "http/")
|
||
decodedURL = strings.TrimPrefix(decodedURL, "https/")
|
||
// Correctly concatenate the protocol with the decoded URL
|
||
decodedURL = protocol + decodedURL
|
||
// Create a new HTTP request to the decoded URL, without copying headers
|
||
req, err := http.NewRequest("GET", decodedURL, nil)
|
||
if err != nil {
|
||
NotFoundHandler(w, r)
|
||
return
|
||
}
|
||
// Send the request and get the response
|
||
client := &http.Client{}
|
||
resp, err := client.Do(req)
|
||
if err != nil || resp.StatusCode != http.StatusOK {
|
||
NotFoundHandler(w, r)
|
||
return
|
||
}
|
||
defer resp.Body.Close()
|
||
// Read the response body into a byte slice
|
||
fileContent, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
NotFoundHandler(w, r)
|
||
return
|
||
}
|
||
setContentType(w, decodedURL)
|
||
// Write file content to response
|
||
w.Write(fileContent)
|
||
return
|
||
}
|
||
|
||
// 统一使用解码后路径
|
||
fullPath := filepath.Join("./files", filePath)
|
||
fileContent, err := GetFileContent(fullPath)
|
||
|
||
// 特殊处理空music.mp3
|
||
isEmptyMusic := (err == nil && len(fileContent) == 0 && strings.HasSuffix(filePath, "/music.mp3"))
|
||
if err != nil || isEmptyMusic {
|
||
// 没有/空的music.mp3文件,直接返回404
|
||
NotFoundHandler(w, r)
|
||
return
|
||
}
|
||
|
||
// 避免重复Content-Type设置
|
||
setContentType(w, filePath)
|
||
w.Write(fileContent)
|
||
}
|
||
|
||
func setContentType(w http.ResponseWriter, path string) {
|
||
ext := strings.ToLower(filepath.Ext(path))
|
||
contentTypes := map[string]string{
|
||
".mp3": "audio/mpeg",
|
||
".wav": "audio/wav",
|
||
".flac": "audio/flac",
|
||
".aac": "audio/aac",
|
||
".ogg": "audio/ogg",
|
||
".m4a": "audio/mp4",
|
||
".amr": "audio/amr",
|
||
".jpg": "image/jpeg",
|
||
".jpeg": "image/jpeg",
|
||
".png": "image/png",
|
||
".gif": "image/gif",
|
||
".bmp": "image/bmp",
|
||
".svg": "image/svg+xml",
|
||
".webp": "image/webp",
|
||
".txt": "text/plain",
|
||
".lrc": "text/plain",
|
||
".mrc": "text/plain",
|
||
".json": "application/json",
|
||
}
|
||
if ct, ok := contentTypes[ext]; ok {
|
||
w.Header().Set("Content-Type", ct)
|
||
} else {
|
||
w.Header().Set("Content-Type", "application/octet-stream")
|
||
}
|
||
}
|