Browse Source

change files

master
godo 9 months ago
parent
commit
75705cb4b7
  1. 1
      godo/cmd/main.go
  2. 98
      godo/localchat/filedown.go
  3. 23
      godo/localchat/filemsg.go
  4. 148
      godo/localchat/getfiles.go

1
godo/cmd/main.go

@ -107,6 +107,7 @@ func OsStart() {
localchatRouter.HandleFunc("/cannelfile", localchat.HandlerCannelFile).Methods(http.MethodPost) localchatRouter.HandleFunc("/cannelfile", localchat.HandlerCannelFile).Methods(http.MethodPost)
localchatRouter.HandleFunc("/accessfile", localchat.HandlerAccessFile).Methods(http.MethodPost) localchatRouter.HandleFunc("/accessfile", localchat.HandlerAccessFile).Methods(http.MethodPost)
localchatRouter.HandleFunc("/getfiles", localchat.HandleGetFiles).Methods(http.MethodPost) localchatRouter.HandleFunc("/getfiles", localchat.HandleGetFiles).Methods(http.MethodPost)
localchatRouter.HandleFunc("/servefile", localchat.HandleServeFile).Methods(http.MethodGet)
localchatRouter.HandleFunc("/sendimage", localchat.HandlerSendImg).Methods(http.MethodPost) localchatRouter.HandleFunc("/sendimage", localchat.HandlerSendImg).Methods(http.MethodPost)
localchatRouter.HandleFunc("/viewimage", localchat.HandleViewImg).Methods(http.MethodGet) localchatRouter.HandleFunc("/viewimage", localchat.HandleViewImg).Methods(http.MethodGet)
localchatRouter.HandleFunc("/setting", localchat.HandleAddr).Methods(http.MethodPost) localchatRouter.HandleFunc("/setting", localchat.HandleAddr).Methods(http.MethodPost)

98
godo/localchat/filedown.go

@ -71,7 +71,7 @@ func downloadFiles(msg UdpMessage) error {
} }
// 处理响应中的文件 // 处理响应中的文件
if err := handleResponse(resp.Body, receiveDir); err != nil { if err := handleResponse(resp.Body, receiveDir, msg.IP); err != nil {
log.Fatalf("Failed to handle response: %v", err) log.Fatalf("Failed to handle response: %v", err)
} }
@ -79,105 +79,63 @@ func downloadFiles(msg UdpMessage) error {
return nil return nil
} }
func handleResponse(reader io.Reader, saveDir string) error { func handleResponse(reader io.Reader, saveDir string, ip string) error {
body, err := io.ReadAll(reader) body, err := io.ReadAll(reader)
if err != nil { if err != nil {
return fmt.Errorf("failed to read response body: %v", err) return fmt.Errorf("failed to read response body: %v", err)
} }
log.Printf("Received file list: %v", string(body)) log.Printf("Received file list: %v", string(body))
var fileList FileList // 解析文件列表
var fileList []FileItem
if err := json.Unmarshal(body, &fileList); err != nil { if err := json.Unmarshal(body, &fileList); err != nil {
return fmt.Errorf("failed to unmarshal file list: %v", err) return fmt.Errorf("failed to unmarshal file list: %v", err)
} }
log.Printf("Received file list: %v", fileList.Files) log.Printf("Received file list: %v", fileList)
for _, filePath := range fileList.Files {
err := saveFileOrFolder(filePath, saveDir)
if err != nil {
return fmt.Errorf("failed to save file or folder: %v", err)
}
}
return nil
}
func saveFileOrFolder(filePath string, saveDir string) error {
relativePath := filePath
localFilePath := filepath.Join(saveDir, relativePath)
// 检查是否为文件夹 for _, file := range fileList {
if _, err := os.Stat(localFilePath); os.IsNotExist(err) { checkpath := filepath.Join(saveDir, file.WritePath)
// 如果不存在,则创建文件夹 if err := os.MkdirAll(checkpath, 0755); err != nil {
err := os.MkdirAll(localFilePath, 0755)
if err != nil {
return fmt.Errorf("failed to create directory: %v", err) return fmt.Errorf("failed to create directory: %v", err)
} }
} else if fileInfo, err := os.Stat(localFilePath); err == nil && fileInfo.IsDir() { if !file.IsDir {
// 如果是文件夹,则递归处理 if err := downloadFile(file.Path, checkpath, ip); err != nil {
err := saveFolder(relativePath, saveDir) return fmt.Errorf("failed to download file: %v", err)
if err != nil { }
return fmt.Errorf("failed to save folder: %v", err)
}
} else {
// 如果是文件,则直接保存
err := saveFile(localFilePath)
if err != nil {
return fmt.Errorf("failed to save file: %v", err)
} }
} }
return nil return nil
} }
func saveFolder(folderName string, saveDir string) error { // downloadFile 下载单个文件
localFolderPath := filepath.Join(saveDir, folderName) func downloadFile(filePath string, checkpath string, ip string) error {
err := os.MkdirAll(localFolderPath, 0755) url := fmt.Sprintf("http://%s:56780/localchat/servefile?path=%s", ip, filePath)
if err != nil {
return fmt.Errorf("failed to create directory: %v", err)
}
// 获取文件夹内容 resp, err := http.Get(url)
folderPath := filepath.Join(saveDir, folderName)
files, err := os.ReadDir(folderPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to read directory: %v", err) return fmt.Errorf("failed to download file: %v", err)
} }
defer resp.Body.Close()
for _, file := range files { if resp.StatusCode != http.StatusOK {
subPath := filepath.Join(folderName, file.Name()) body, _ := io.ReadAll(resp.Body)
if file.IsDir() { return fmt.Errorf("unexpected status code: %v, body: %s", resp.StatusCode, body)
err := saveFolder(subPath, localFolderPath)
if err != nil {
return fmt.Errorf("failed to save folder: %v", err)
}
} else {
err := saveFile(filepath.Join(localFolderPath, file.Name()))
if err != nil {
return fmt.Errorf("failed to save file: %v", err)
}
}
} }
return nil // 保存文件
} fileName := filepath.Join(checkpath, filepath.Base(filePath))
func saveFile(filePath string) error {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
// 创建本地文件 out, err := os.Create(fileName)
localFile, err := os.Create(filePath)
if err != nil { if err != nil {
return fmt.Errorf("failed to create file: %v", err) return fmt.Errorf("failed to create file: %v", err)
} }
defer localFile.Close() defer out.Close()
_, err = io.Copy(localFile, file) _, err = io.Copy(out, resp.Body)
if err != nil { if err != nil {
return fmt.Errorf("failed to copy file: %v", err) return fmt.Errorf("failed to write file: %v", err)
} }
fmt.Printf("File downloaded: %s\n", fileName)
return nil return nil
} }

23
godo/localchat/filemsg.go

@ -1,3 +1,26 @@
// MIT License
//
// Copyright (c) 2024 godoos.com
// Email: xpbb@qq.com
// GitHub: github.com/phpk/godoos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package localchat package localchat
import ( import (

148
godo/localchat/getfiles.go

@ -1,17 +1,45 @@
// MIT License
//
// Copyright (c) 2024 godoos.com
// Email: xpbb@qq.com
// GitHub: github.com/phpk/godoos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package localchat package localchat
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"godo/libs" "godo/libs"
"io"
"log" "log"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
) )
type FileList struct { // FileItem 表示文件或文件夹
Files []string `json:"fileList"` type FileItem struct {
Path string `json:"path"`
IsDir bool `json:"isDir"`
Filename string `json:"filename"`
WritePath string `json:"writePath"`
} }
func HandleGetFiles(w http.ResponseWriter, r *http.Request) { func HandleGetFiles(w http.ResponseWriter, r *http.Request) {
@ -20,7 +48,7 @@ func HandleGetFiles(w http.ResponseWriter, r *http.Request) {
return return
} }
var fileList FileList var fileList []string
err := json.NewDecoder(r.Body).Decode(&fileList) err := json.NewDecoder(r.Body).Decode(&fileList)
if err != nil { if err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest) http.Error(w, "Invalid request body", http.StatusBadRequest)
@ -32,18 +60,42 @@ func HandleGetFiles(w http.ResponseWriter, r *http.Request) {
baseDir, err := libs.GetOsDir() baseDir, err := libs.GetOsDir()
if err != nil { if err != nil {
log.Printf("Failed to get OS directory: %v", err) log.Printf("Failed to get OS directory: %v", err)
http.Error(w, "Failed to get OS directory", http.StatusInternalServerError)
return return
} }
// 用于存储文件列表 // 用于存储文件列表
var files []string var files []FileItem
for _, filePath := range fileList.Files { for _, filePath := range fileList {
fp := filepath.Join(baseDir, filePath) fp := filepath.Join(baseDir, filePath)
if err := serveDirectory(w, r, fp, &files); err != nil {
http.Error(w, fmt.Sprintf("Failed to serve directory: %v", err), http.StatusInternalServerError) fileInfo, err := os.Stat(fp)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to stat file: %v", err), http.StatusInternalServerError)
return return
} }
writePath := calculateWritePath(fp, baseDir)
if fileInfo.IsDir() {
files = append(files, FileItem{
Path: fp,
IsDir: true,
Filename: filepath.Base(fp),
WritePath: writePath,
})
if err := walkDirectory(fp, &files, writePath); err != nil {
http.Error(w, fmt.Sprintf("Failed to serve directory: %v", err), http.StatusInternalServerError)
return
}
} else {
files = append(files, FileItem{
Path: fp,
IsDir: false,
Filename: filepath.Base(fp),
WritePath: writePath,
})
}
} }
// 将文件列表编码为 JSON 并返回 // 将文件列表编码为 JSON 并返回
@ -56,43 +108,75 @@ func HandleGetFiles(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Write(jsonData) w.Write(jsonData)
} }
func calculateWritePath(filePath, baseDir string) string {
func serveDirectory(w http.ResponseWriter, r *http.Request, dirPath string, files *[]string) error { relativePath, err := filepath.Rel(baseDir, filePath)
filesInDir, err := os.ReadDir(dirPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to read directory: %v", err) log.Printf("Failed to calculate relative path: %v", err)
return ""
} }
return filepath.Dir(relativePath)
for _, f := range filesInDir { }
filePath := filepath.Join(dirPath, f.Name()) func walkDirectory(rootPath string, files *[]FileItem, writePath string) error {
if f.IsDir() { return filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
if err := serveDirectory(w, r, filePath, files); err != nil { if err != nil {
return err return fmt.Errorf("failed to walk directory: %v", err)
}
} else {
*files = append(*files, filePath)
} }
}
return nil isDir := info.IsDir()
relativePath, err := filepath.Rel(rootPath, path)
if err != nil {
log.Printf("Failed to calculate relative path: %v", err)
return fmt.Errorf("Failed to calculate relative path")
}
currentWritePath := filepath.Join(writePath, filepath.Dir(relativePath))
*files = append(*files, FileItem{
Path: path,
IsDir: isDir,
Filename: filepath.Base(path),
WritePath: currentWritePath,
})
return nil
})
} }
func ServeFile(w http.ResponseWriter, r *http.Request, filePath string) error { func HandleServeFile(w http.ResponseWriter, r *http.Request) {
file, err := os.Open(filePath) // 从 URL 中获取 filePath 参数
if err != nil { filePath := r.URL.Query().Get("path")
return fmt.Errorf("failed to open file: %v", err)
if filePath == "" {
http.Error(w, "Missing filePath parameter", http.StatusBadRequest)
return
} }
defer file.Close()
fileInfo, err := file.Stat() fileInfo, err := os.Stat(filePath)
if err != nil { if err != nil {
return fmt.Errorf("failed to stat file: %v", err) http.Error(w, fmt.Sprintf("Failed to stat file: %v", err), http.StatusInternalServerError)
return
} }
if fileInfo.IsDir() { if fileInfo.IsDir() {
return serveDirectory(w, r, filePath, nil) http.Error(w, "Cannot download a directory", http.StatusBadRequest)
return
}
// 打开文件
file, err := os.Open(filePath)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to open file: %v", err), http.StatusInternalServerError)
return
} }
defer file.Close()
// 设置响应头
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileInfo.Name()))
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
http.ServeContent(w, r, fileInfo.Name(), fileInfo.ModTime(), file) // 复制文件内容到响应体
return nil _, err = io.Copy(w, file)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to copy file content: %v", err), http.StatusInternalServerError)
return
}
} }

Loading…
Cancel
Save