package handler import ( "encoding/json" "fmt" "html/template" "io" "mime" "net" "path" "strings" "time" "net/http" "net/rpc" "os" "path/filepath" "github.com/chyok/st/config" "github.com/chyok/st/internal/transfer" "github.com/chyok/st/internal/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"]) fmt.Printf("Downloading [%s]...\n", filename) dirPath := filepath.Dir(filename) err = os.MkdirAll(dirPath, os.ModePerm) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } out, err := os.Create(filename) 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(filename) } 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) } } // udp 方式发送zip文件 func SendZip(w http.ResponseWriter, r *http.Request) { r.ParseForm() // 选择文件,并生成zip包 // 文件 zipfarr := r.Form["zipfiles"] // 服务器ip地址 serip := r.Form["serverip"] if serip[0] == "" { http.Error(w, "remote server ip is blank!", http.StatusInternalServerError) return } // 选中的路径,可以为空 wtculpath := r.Form["curpath"] // 实际路径 realFilePath := filepath.Join(config.G.FilePath, wtculpath[0]) // zip 文件名 zpFileName := "BIU_" + time.Now().Format("20060102_150405") + ".zip" // 创建zip 异步? taskId := make(chan string) go func() { util.CompressToZip(zpFileName, realFilePath, zipfarr) taskId <- "arcok" // fmt.Fprintln(w, "create archive:", err) }() // go util.CompressToZip(zpFileName, realFilePath, zipfarr) fmt.Println("archive is createding...") // 当前运行的目录 // ZIP 文件的实际路径 ziprl := path.Join("./files/", zpFileName) // zip 创建成功后 rest := <-taskId // 有压缩包 才可以操作 if strings.EqualFold(strings.ToLower(rest), "arcok") { fmt.Println("archive is sending...") // 创建udp 渠道发送数据 // 1、获取udp addr remoteAddr, err := net.ResolveUDPAddr("udp", serip[0]+":9099") if err != nil { fmt.Printf("Failed to resolve %s: %v\n", serip[0], err) return } // 2、 监听端口 conn, err := net.DialUDP("udp", nil, remoteAddr) if err != nil { fmt.Printf("Failed to dial %s: %v\n", serip[0], err) return } defer conn.Close() // 3、在端口发送数据 message := fmt.Sprintf("%s%s%s", config.G.DeviceName, "|", "sender") // 向链接通道发送数据 数据包头 conn.Write([]byte(message)) // 发送文件 go func() { err := transfer.SendFiles(ziprl, fmt.Sprintf("http://%s", remoteAddr)) if err != nil { fmt.Printf("Send file to %s error: %s\n", remoteAddr, err) } }() // 页面上显示 fmt.Fprintf(w, "File:%s,been sent successfully.", zpFileName) } else { fmt.Println("archive is not exist!!!") } } // 下载zip func Downzip(w http.ResponseWriter, r *http.Request) { r.ParseForm() // 选择文件,并生成zip包 // 文件 zipfarr := r.Form["zipfiles"] // 服务器ip地址 serip := r.Form["serverip"] if serip[0] == "" { http.Error(w, "remote server ip is blank!", http.StatusInternalServerError) return } // fmt.Println(r.Form) // 选中的路径 wtculpath := r.Form["curpath"] // 实际路径 realFilePath := filepath.Join(config.G.FilePath, wtculpath[0]) // zip 文件名 zpFileName := "BIU_" + time.Now().Format("20060102_150405") + ".zip" // 创建zip err := util.CompressToZip(zpFileName, realFilePath, zipfarr) if err != nil { fmt.Println("create zip error", err) } // 停止下,检查zip是否创建成功 time.Sleep(1200) _, err = os.Stat(realFilePath + "/" + zpFileName) if err != nil { // 调用httpd rpc fmt.Println("RPC Server IP:", serip[0]) rpcServer := serip[0] + ":9080" client, err := rpc.DialHTTP("tcp", rpcServer) if err != nil { fmt.Println("Http rpc error :", err) } // 组装url 静态文件 // url := "http://" + config.G.LocalIP + ":" + config.G.Port + "/files/" + zpFileName // 动态 url := "http://" + config.G.LocalIP + ":" + config.G.Port + "/files?zp=" + zpFileName args := Args{url, zpFileName} var reply int err = client.Call("Rbup.DecompressZip", args, &reply) if err != nil { fmt.Println("rpc call error :", err) // 输出执行结果 fmt.Fprintln(w, "rpc call error:", err) } else { // 输出下载链接 fmt.Fprintf(w, "%s
\n", url, url) fmt.Fprintf(w, "RPC execute result :%d
\n", reply) } } else { fmt.Fprintf(w, "archive is created faild") } } // 文件下载 func Dfiles(w http.ResponseWriter, r *http.Request) { // url中获取参数 query := r.URL.Query() zpfile := query.Get("zp") // 实际路径 zpFileName := filepath.Join(config.G.FilePath, "/files/", zpfile) // fileInfo, err := os.Stat(zpFileName) // if err != nil { // http.Error(w, err.Error(), http.StatusInternalServerError) // return // } // fmt.Println(fileInfo) http.ServeFile(w, r, zpFileName) } // 发送拦截 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 //Rundir string IsDir bool FileName string DownloadPath string UrlPath string Files []os.DirEntry }{ DeviceName: config.G.DeviceName, //Rundir: config.G.FilePath, 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) } // fmt.Print("fdata:%v", data) // 文件列表模板 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) } }