diff --git a/frontend/src/stores/localchat.ts b/frontend/src/stores/localchat.ts index 5956d3e..fcee975 100644 --- a/frontend/src/stores/localchat.ts +++ b/frontend/src/stores/localchat.ts @@ -50,7 +50,7 @@ export const useLocalChatStore = defineStore('localChatStore', () => { addText(msg) } if (msg.type === "image"){ - + console.log(msg) } if (msg.type === "fileSending"){ @@ -403,123 +403,7 @@ export const useLocalChatStore = defineStore('localChatStore', () => { await updateContentList(saveMsg) } - //上传文件资源 - // async function uploadFile(paths: any) { - // if (chatTargetId.value < 1) { - // return - // } - // const targetUser = userList.value.find((d: any) => d.ip === chatTargetIp.value) - // if (!targetUser.isOnline) { - // notifyError("The user is not online!"); - // return; - // } - // if (!hostInfo.value || !hostInfo.value.ip) { - // notifyError("Please wait for a moment"); - // return; - // } - // //console.log(paths) - // const formData = new FormData(); - // const errstr: any = [] - // const files: any = [] - // for (let i = 0; i < paths.length; i++) { - // const content = await sys?.fs.readFile(paths[i]); - // let blobContent; - // if (!content || content == '') { - // errstr.push(paths[i] + " is empty") - // continue - // } - // if (content instanceof ArrayBuffer) { - // blobContent = new Blob([content]); - // } - // else if (typeof content === 'string') { - // if (isBase64(content)) { - // const base64 = base64ToBuffer(content); - // blobContent = new Blob([base64]); - // } else { - // blobContent = new Blob([content], { type: "text/plain;charset=utf-8" }); - // } - // } - // else { - // errstr.push(paths[i] + " type is error") - // continue - // } - // const fileName = paths[i].split("/").pop() - // files.push({ - // name: fileName, - // path: paths[i], - // ext: fileName.split(".").pop(), - // }) - // //files.push(blobContent); - // formData.append(`files`, blobContent, fileName); - // } - // if (errstr.length > 0) { - // errstr.forEach((d: any) => { - // notifyError(d); - // }) - // return - // } - // //formData.append("files", files); - // formData.append("ip", hostInfo.value.ip); - // formData.append("hostname", hostInfo.value.hostname); - // //console.log(formData) - // const postUrl = `http://${targetUser.ip}:56780/localchat/upload` - // const res = await fetch(postUrl, { - // method: "POST", - // body: formData, - // }); - // if (!res.ok) { - // console.log(res); - // notifyError("Upload error!"); - // return; - // } - // const saveMsg: any = { - // type: 'file', - // targetId: targetUser.id, - // targetIp: targetUser.ip, - // content: files, - // reciperInfo: toRaw(targetUser), - // createdAt: Date.now(), - // isMe: false, - // isRead: true, - // status: 'reciped' - // } - // //console.log(saveMsg) - // await db.addOne('chatmsg', saveMsg) - // msgList.value.push(saveMsg) - - // notifySuccess("upload success!"); - // } - - - //async function addUser(ip: string) { - // if (!isValidIP(ip)) { - // notifyError("请输入正确的IP地址"); - // return - // } - // const postUrl = `http://${ip}:56780/localchat/check` - // const completion = await fetch(postUrl) - // if (!completion.ok) { - // notifyError('用户不在线') - // } else { - // const res = await completion.json() - // const data = res.data - // data.createdAt = Date.now() - // data.updatedAt = Date.now() - // data.isOnline = true - // data.username = data.hostname - // if(ip != data.ip){ - // notifyError(`IP地址不一致,可能会存在不通的问题`) - // } - // data.ip = ip - // if (!OutUserList.value.some((item: any) => item.ip === data.ip)) { - // OutUserList.value.push(data); - // await getUserList() - // } - // showAddUser.value = false - - // } - //} return { userList, navList, diff --git a/godo/localchat/file.go b/godo/localchat/file.go index 8b25af0..3de76aa 100644 --- a/godo/localchat/file.go +++ b/godo/localchat/file.go @@ -33,6 +33,7 @@ import ( "net/http" "os" "path/filepath" + "sync" "time" ) @@ -46,6 +47,7 @@ type FileChunk struct { Checksum uint32 `json:"checksum"` Timestamp time.Time `json:"timestamp"` Filename string `json:"filename"` + Filesize int64 `json:"filesize"` } func HandlerApplySendFile(w http.ResponseWriter, r *http.Request) { @@ -146,7 +148,7 @@ func handleFile(filePath string, toIp string, message UdpMessage) { numChunks := (fileSize + fileSize - 1) / fileSize // 发送文件 - SendFile(file, int(numChunks), toIp, message) + SendFile(file, int(numChunks), toIp, fileSize, message) } func handleDirectory(dirPath string, toIp string, message UdpMessage) { @@ -163,75 +165,89 @@ func handleDirectory(dirPath string, toIp string, message UdpMessage) { log.Fatalf("Failed to walk directory: %v", err) } } -func SendFile(file *os.File, numChunks int, toIp string, message UdpMessage) { +func SendFile(file *os.File, numChunks int, toIp string, fSize int64, message UdpMessage) { + var wg sync.WaitGroup + mu := &sync.Mutex{} + // 逐块读取文件并发送 for i := 0; i < numChunks; i++ { - var chunkData [fileSize]byte - n, err := file.Read(chunkData[:]) - if err != nil && err != io.EOF { - log.Fatalf("Failed to read file chunk: %v", err) - } + wg.Add(1) + go func(index int) { + defer wg.Done() - // 创建文件块 - chunk := FileChunk{ - ChunkIndex: i, - Data: chunkData[:n], - Checksum: calculateChecksum(chunkData[:n]), - Timestamp: time.Now(), - Filename: filepath.Base(file.Name()), - } - message.Message = chunk - // 将文件块转换为 JSON 格式 - data, err := json.Marshal(message) - if err != nil { - log.Fatalf("Failed to marshal chunk: %v", err) - } + var chunkData [fileSize]byte + n, err := file.Read(chunkData[:]) + if err != nil && err != io.EOF { + log.Fatalf("Failed to read file chunk: %v", err) + } - // 发送文件块 - //addr, err := net.ResolveUDPAddr("udp4", toIp+":56780") - port := "56780" - addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", toIp, port)) - if err != nil { - log.Fatalf("Failed to resolve UDP address: %v", err) - } + // 创建文件块 + chunk := FileChunk{ + ChunkIndex: index, + Data: chunkData[:n], + Checksum: calculateChecksum(chunkData[:n]), + Timestamp: time.Now(), + Filename: filepath.Base(file.Name()), + Filesize: fSize, + } + message.Message = chunk - conn, err := net.DialUDP("udp4", nil, addr) - if err != nil { - log.Fatalf("Failed to dial UDP address: %v", err) - } - defer conn.Close() + // 将文件块转换为 JSON 格式 + data, err := json.Marshal(message) + if err != nil { + log.Fatalf("Failed to marshal chunk: %v", err) + } - _, err = conn.Write(data) - if err != nil { - log.Printf("Failed to write data: %v", err) - } + // 发送文件块 + port := "56780" + addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", toIp, port)) + if err != nil { + log.Fatalf("Failed to resolve UDP address: %v", err) + } + + conn, err := net.DialUDP("udp4", nil, addr) + if err != nil { + log.Fatalf("Failed to dial UDP address: %v", err) + } + defer conn.Close() - fmt.Printf("发送文件块 %d 到 %s 成功\n", i, toIp) + _, err = conn.Write(data) + if err != nil { + log.Printf("Failed to write data: %v", err) + } + + mu.Lock() + fmt.Printf("发送文件块 %d 到 %s 成功\n", index, toIp) + mu.Unlock() + }(i) } + + wg.Wait() } -func ReceiveFile(msg UdpMessage) { +func ReceiveFile(msg UdpMessage) (string, error) { chunk := msg.Message.(FileChunk) // 验证校验和 calculatedChecksum := calculateChecksum(chunk.Data) if calculatedChecksum != chunk.Checksum { fmt.Printf("Checksum mismatch for chunk %d from %s\n", chunk.ChunkIndex, msg.IP) - return + return "", fmt.Errorf("checksum mismatch") } baseDir, err := libs.GetOsDir() if err != nil { log.Printf("Failed to get OS directory: %v", err) - return + return "", fmt.Errorf("failed to get OS directory") } // 创建接收文件的目录 - receiveDir := filepath.Join(baseDir, "C", "Users", "Reciv", time.Now().Format("2006-01-02")) + resPath := filepath.Join("C", "Users", "Reciv", time.Now().Format("2006-01-02")) + receiveDir := filepath.Join(baseDir, resPath) if !libs.PathExists(receiveDir) { err := os.MkdirAll(receiveDir, 0755) if err != nil { log.Printf("Failed to create receive directory: %v", err) - return + return "", fmt.Errorf("failed to create receive directory") } } @@ -243,7 +259,7 @@ func ReceiveFile(msg UdpMessage) { file, err := os.Create(filePath) if err != nil { log.Printf("Failed to create file: %v", err) - return + return "", fmt.Errorf("failed to create file") } defer file.Close() } @@ -252,7 +268,7 @@ func ReceiveFile(msg UdpMessage) { file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644) if err != nil { log.Printf("Failed to open file: %v", err) - return + return "", fmt.Errorf("failed to open file") } defer file.Close() @@ -260,10 +276,20 @@ func ReceiveFile(msg UdpMessage) { _, err = file.Write(chunk.Data) if err != nil { log.Printf("Failed to write data to file: %v", err) - return + return "", fmt.Errorf("failed to write data to file") + } + fileInfo, err := os.Stat(filePath) + if err != nil { + log.Printf("Failed to stat file: %v", err) + return "", fmt.Errorf("failed to stat file") + } + if fileInfo.Size() == chunk.Filesize { + fmt.Println("文件接收完成且大小一致") + return filePath, nil + } else { + fmt.Println("文件大小不一致") + return "", fmt.Errorf("file size mismatch") } - - fmt.Printf("接收到文件块 %d 从 %s 成功\n", chunk.ChunkIndex, msg.IP) } func calculateChecksum(data []byte) uint32 { checksum := uint32(0) diff --git a/godo/localchat/image.go b/godo/localchat/image.go index a7396e6..039890a 100644 --- a/godo/localchat/image.go +++ b/godo/localchat/image.go @@ -26,11 +26,9 @@ package localchat import ( "encoding/json" - "fmt" "godo/libs" "io" "log" - "net" "net/http" "net/url" "os" @@ -60,10 +58,10 @@ func HandlerSendImg(w http.ResponseWriter, r *http.Request) { log.Printf("GetOsDir error: %v", err) return } - log.Printf("send image to %v", msg.Message) - log.Printf("Type of msg.Message: %T", msg.Message) + // log.Printf("send image to %v", msg.Message) + // log.Printf("Type of msg.Message: %T", msg.Message) paths, ok := msg.Message.([]interface{}) - log.Printf("paths: %v", paths) + //log.Printf("paths: %v", paths) if !ok { libs.ErrorMsg(w, "HandleMessage message error") return @@ -78,7 +76,7 @@ func HandlerSendImg(w http.ResponseWriter, r *http.Request) { // 处理多张图片 if fileInfo, err := os.Stat(filePath); err == nil { if !fileInfo.IsDir() { - sendImage(filePath, toIp, msg) + handleFile(filePath, toIp, msg) } } else { continue @@ -87,121 +85,65 @@ func HandlerSendImg(w http.ResponseWriter, r *http.Request) { libs.SuccessMsg(w, nil, "图片发送成功") } -func sendImage(filePath string, toIp string, message UdpMessage) { - // 打开文件 - file, err := os.Open(filePath) - if err != nil { - log.Fatalf("Failed to open file: %v", err) - } - defer file.Close() - - // 获取文件大小 - fileInfo, err := file.Stat() - if err != nil { - log.Fatalf("Failed to get file info: %v", err) - } - fileSize := fileInfo.Size() - - // 读取整个文件 - data := make([]byte, fileSize) - _, err = file.Read(data) - if err != nil { - log.Fatalf("Failed to read file: %v", err) - } - - // 创建文件块 - chunk := FileChunk{ - Data: data, - Checksum: calculateChecksum(data), - Timestamp: time.Now(), - Filename: filepath.Base(file.Name()), - } - message.Message = chunk - // 将文件块转换为 JSON 格式 - data, err = json.Marshal(message) - if err != nil { - log.Fatalf("Failed to marshal chunk: %v", err) - } - - // 发送文件块 - port := "56780" - addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", toIp, port)) - if err != nil { - log.Fatalf("Failed to resolve UDP address: %v", err) - } - - conn, err := net.DialUDP("udp4", nil, addr) - if err != nil { - log.Fatalf("Failed to dial UDP address: %v", err) - } - defer conn.Close() - - _, err = conn.Write(data) - if err != nil { - log.Printf("Failed to write data: %v", err) - } - - fmt.Printf("发送图片 %s 到 %s 成功\n", filePath, toIp) -} -func ReceiveImg(msg UdpMessage) (string, error) { - chunk := msg.Message.(FileChunk) - - // 验证校验和 - calculatedChecksum := calculateChecksum(chunk.Data) - if calculatedChecksum != chunk.Checksum { - fmt.Printf("Checksum mismatch for image from %s\n", msg.IP) - return "", fmt.Errorf("checksum mismatch") - } - - baseDir, err := libs.GetOsDir() - if err != nil { - log.Printf("Failed to get OS directory: %v", err) - return "", err - } - - // 创建接收文件的目录 - resPath := filepath.Join("C", "Users", "Reciv", time.Now().Format("2006-01-02")) - receiveDir := filepath.Join(baseDir, resPath) - if !libs.PathExists(receiveDir) { - err := os.MkdirAll(receiveDir, 0755) - if err != nil { - log.Printf("Failed to create receive directory: %v", err) - return "", err - } - } - - // 确定文件路径 - filePath := filepath.Join(receiveDir, chunk.Filename) - - // 如果文件不存在,则创建新文件 - if _, err := os.Stat(filePath); os.IsNotExist(err) { - file, err := os.Create(filePath) - if err != nil { - log.Printf("Failed to create file: %v", err) - return "", err - } - defer file.Close() - } - - // 打开或追加到现有文件 - file, err := os.OpenFile(filePath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - log.Printf("Failed to open file: %v", err) - return "", err - } - defer file.Close() - - // 写入数据 - _, err = file.Write(chunk.Data) - if err != nil { - log.Printf("Failed to write data to file: %v", err) - return "", err - } - - fmt.Printf("接收到图片 %s 从 %s 成功\n", filePath, msg.IP) - resFilePath := filepath.Join(resPath, chunk.Filename) - return resFilePath, nil -} +// func ReceiveImg(msg UdpMessage) (string, error) { +// chunk := msg.Message.(FileChunk) + +// // 验证校验和 +// calculatedChecksum := calculateChecksum(chunk.Data) +// if calculatedChecksum != chunk.Checksum { +// fmt.Printf("Checksum mismatch for image from %s\n", msg.IP) +// return "", fmt.Errorf("checksum mismatch") +// } + +// baseDir, err := libs.GetOsDir() +// if err != nil { +// log.Printf("Failed to get OS directory: %v", err) +// return "", err +// } + +// // 创建接收文件的目录 +// resPath := filepath.Join("C", "Users", "Reciv", time.Now().Format("2006-01-02")) +// receiveDir := filepath.Join(baseDir, resPath) +// if !libs.PathExists(receiveDir) { +// err := os.MkdirAll(receiveDir, 0755) +// if err != nil { +// log.Printf("Failed to create receive directory: %v", err) +// return "", err +// } +// } + +// // 确定文件路径 +// filePath := filepath.Join(receiveDir, chunk.Filename) + +// // 如果文件不存在,则创建新文件 +// if _, err := os.Stat(filePath); os.IsNotExist(err) { +// file, err := os.Create(filePath) +// if err != nil { +// log.Printf("Failed to create file: %v", err) +// return "", err +// } +// defer file.Close() +// } + +// // 打开或追加到现有文件 +// file, err := os.OpenFile(filePath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) +// if err != nil { +// log.Printf("Failed to open file: %v", err) +// return "", err +// } +// defer file.Close() + +// // 写入数据 +// _, err = file.Write(chunk.Data) +// if err != nil { +// log.Printf("Failed to write data to file: %v", err) +// return "", err +// } + +// fmt.Printf("接收到图片 %s 从 %s 成功\n", filePath, msg.IP) +// resFilePath := filepath.Join(resPath, chunk.Filename) +// return resFilePath, nil +// } func HandleViewImg(w http.ResponseWriter, r *http.Request) { img := r.URL.Query().Get("img") if img == "" { diff --git a/godo/localchat/server.go b/godo/localchat/server.go index 0cd6b9d..f5233c3 100644 --- a/godo/localchat/server.go +++ b/godo/localchat/server.go @@ -118,7 +118,7 @@ func UdpServer() { continue } if udpMsg.Type == "image" { - filePath, err := ReceiveImg(udpMsg) + filePath, err := ReceiveFile(udpMsg) if err != nil { log.Printf("error receiving image: %v", err) continue