package handler import ( "encoding/json" "fmt" "html/template" "io" "mime" "strings" "time" "net/http" "os" "path/filepath" "xtcfs/config" "xtcfs/util" "github.com/chyok/st/web" ) // rpc功能 压缩包的结构体 // 文件路径 文件名 type Args struct { Zpfile, Fname string } // 获取文件大小的接口 type Size interface { Size() int64 } // 获取文件信息的接口 type Stat interface { Stat() (os.FileInfo, error) } // 显示状态 func ReceiveStatus(w http.ResponseWriter, r *http.Request) { switch r.Method { // 显示当前传送文件的大小 case http.MethodPost: file, _, err := r.FormFile("file") if err != nil { http.Error(w, err.Error(), 500) return } if statInterface, ok := file.(Stat); ok { fileInfo, _ := statInterface.Stat() fmt.Fprintf(w, "上传文件的大小为: %d", fileInfo.Size()) } if sizeInterface, ok := file.(Size); ok { fmt.Fprintf(w, "上传文件的大小为: %d", sizeInterface.Size()) } return } // fmt.Fprintf(w, r.Method) } // 接收 上传 func ReceiveHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: // serve upload page for receive // tmpl, err := template.New("index").Parse(web.UploadPage) // if err != nil { // http.Error(w, err.Error(), http.StatusInternalServerError) // return // } // // 获取当前设备名称 // err = tmpl.Execute(w, config.G.DeviceName) // if err != nil { // http.Error(w, err.Error(), http.StatusInternalServerError) // } // fmt.Fprintf(w, "%s:接收文件中...", r.Host) case http.MethodPost: // receive file and save file, header, err := r.FormFile("file") if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } defer file.Close() _, params, err := mime.ParseMediaType(header.Header.Get("Content-Disposition")) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } filename := filepath.FromSlash(params["filename"]) // fpath bfpath := r.Header.Get("Fpath") //debug fmt.Printf("header params:%v\n", r.Header) fmt.Printf("fpath is:%s,afbs64 is:%s\n", bfpath, util.Base64dec(bfpath)) fmt.Printf("Downloading [%s]...\n", filename) // 拼装完整路径 nfname := filepath.Join(config.G.FilePath, util.Base64dec(bfpath)) dirPath := filepath.Dir(nfname) //dirPath := filepath.Dir(filename) err = os.MkdirAll(dirPath, os.ModePerm) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // 检查文件是否存在 if util.IsFileExist(nfname) { // 文件重命名 ddname := filename + "_backup_" + time.Now().Format("20060102150405") // 加上路径 fnewname := filepath.Join(dirPath, ddname) os.Rename(nfname, fnewname) } out, err := os.Create(nfname) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // 如果单文件,需要先备份 // 如果收到的是zip文件,自动给解压缩 suf := strings.Split(filename, ".") if suf[1] == "zip" { // 传入实际路径 go util.DecompressZip(nfname) } fmt.Printf("[√] Download [%s] Success.\n", filename) // default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } // 输出接收结果 // fmt.Fprintf(w, "接收成功,并已经完成解压缩") } // 文件服务 func FileServerHandler(w http.ResponseWriter, r *http.Request) { currentPath := config.G.FilePath fileInfo, err := os.Stat(currentPath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } basePath := filepath.Base(currentPath) if fileInfo.IsDir() { path := r.URL.Path[len("/download/"+basePath):] fullPath := filepath.Join(currentPath, path) http.ServeFile(w, r, fullPath) } else { http.ServeFile(w, r, currentPath) } } // 发送拦截 func SendHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: // serve download page for send realFilePath := filepath.Join(config.G.FilePath, r.URL.Path[1:]) downloadPath := filepath.Join(filepath.Base(config.G.FilePath), r.URL.Path[1:]) fileInfo, err := os.Stat(realFilePath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } data := struct { DeviceName string IsDir bool FileName string DownloadPath string UrlPath string Files []os.DirEntry }{ DeviceName: config.G.DeviceName, DownloadPath: downloadPath, UrlPath: strings.TrimSuffix(r.URL.Path, "/"), } if fileInfo.IsDir() { data.IsDir = true // 遍历目录 files, err := os.ReadDir(realFilePath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } data.Files = files } else { data.FileName = filepath.Base(realFilePath) } // 文件列表模板 tmpl, err := template.New("download").Parse(web.DownloadPage) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } err = tmpl.Execute(w, data) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } case http.MethodPost: // return file or folder information in JSON format for convenient send to the recipient currentPath := config.G.FilePath fileInfo, err := os.Stat(currentPath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var pathInfo struct { Type string `json:"type"` Paths []string `json:"paths"` } if fileInfo.IsDir() { files, err := util.GetDirFilePaths(currentPath, true) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } pathInfo.Paths = files pathInfo.Type = "dir" } else { pathInfo.Paths = []string{filepath.Base(currentPath)} pathInfo.Type = "file" } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(pathInfo) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } }