Browse Source

change files

master
godo 9 months ago
parent
commit
7d9e9165e7
  1. 8
      frontend/src/components/localchat/ChatContent.vue
  2. 73
      frontend/src/stores/localchat.ts
  3. 389
      godo/localchat/file.go
  4. 8
      godo/localchat/server.go

8
frontend/src/components/localchat/ChatContent.vue

@ -111,7 +111,7 @@ async function scroll({ scrollTop }: { scrollTop: number }) {
<span v-if="item.content.status === 'accessing'"> <span v-if="item.content.status === 'accessing'">
接收中 接收中
</span> </span>
<span v-if="item.content.status === 'accessed'"> <span v-if="item.content.status === 'confirm'">
已接收 已接收
</span> </span>
</template> </template>
@ -129,10 +129,10 @@ async function scroll({ scrollTop }: { scrollTop: number }) {
</div> </div>
<template #footer> <template #footer>
<span v-if="item.content.status === 'apply'">对方确认中</span> <span v-if="item.content.status === 'apply'">对方确认中</span>
<span v-if="item.content.status === 'confirm'"> <span v-if="item.content.status === 'fileAccessed'">
对方已接收 对方已确认
</span> </span>
<span v-if="item.content.status === 'cannel'"> <span v-if="item.content.status === 'fileCannel'">
对方已拒绝 对方已拒绝
</span> </span>
</template> </template>

73
frontend/src/stores/localchat.ts

@ -57,7 +57,7 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
else if (msg.type === "fileSending"){ else if (msg.type === "fileSending"){
addText(msg) addText(msg)
} }
else if( msg.type === "fileCannel") { else if( msg.type === "fileCannel" || msg.type === "fileAccessed") {
changeMsg(msg) changeMsg(msg)
} }
}) })
@ -261,42 +261,6 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
} }
await getUserList() await getUserList()
} }
const addFile = async (data: any) => {
const targetUser: any = await getTargetUser(data)
const files: any = []
data.fileList.forEach((d: any) => {
d.save_path = d.save_path.replace(/\\/g, "/");
files.push({
name: d.name,
path: d.save_path,
ext: d.save_path.split('.').pop(),
content: d.content
})
})
const saveMsg: any = {
type: 'file',
targetId: targetUser.id,
targetIp: targetUser.ip,
content: files,
reciperInfo: data.senderInfo,
createdAt: Date.now(),
isMe: false,
isRead: false,
status: 'reciped'
}
if (targetUser.id === chatTargetId.value) {
saveMsg.readAt = Date.now()
saveMsg.isRead = true
msgList.value.push(saveMsg)
}
//console.log(saveMsg)
await db.addOne('chatmsg', saveMsg)
//await getMsgList()
await updateContentList(saveMsg)
handleSelect(1)
}
const getTargetUser = async (data: any) => { const getTargetUser = async (data: any) => {
let targetUser: any = userList.value.find((d: any) => d.ip === data.ip) let targetUser: any = userList.value.find((d: any) => d.ip === data.ip)
@ -432,21 +396,42 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
notifyError("确认失败!") notifyError("确认失败!")
} else { } else {
item.content.status = 'cannel' item.content.status = 'cannel'
await db.update('chatmsg', item.id, item) console.log(item)
await db.update('chatmsg', item.id, toRaw(item))
await updateContentList(item) await updateContentList(item)
notifySuccess("确认成功!") notifySuccess("确认成功!")
} }
} }
async function changeMsg(msg:any){ async function changeMsg(msg:any){
console.log(msg) //console.log(msg)
const msgId = msg.message const msgId = msg.message
const item = await db.getOne('chatmsg', msgId) const item = await db.getOne('chatmsg', msgId)
item.content.status = 'cannel' //console.log(item)
item.content.status = msg.type
await db.update('chatmsg', item.id, item) await db.update('chatmsg', item.id, item)
await updateContentList(item) await getMsgList()
} }
async function accessFile(item:any){ async function accessFile(item:any){
const messages = {
type: 'accessFile',
message: item.content,
ip: item.targetIp
}
const postUrl = `${config.apiUrl}/localchat/accessfile`
const coms = await fetch(postUrl, {
method: "POST",
body: JSON.stringify(messages),
})
if (!coms.ok) {
//console.log(coms)
notifyError("确认失败!")
} else {
item.content.status = 'accessing'
//console.log(item)
await db.update('chatmsg', item.id, toRaw(item))
await getMsgList()
//notifySuccess("确认成功!")
}
} }
return { return {
@ -463,7 +448,6 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
showChooseFile, showChooseFile,
pageSize, pageSize,
showAddUser, showAddUser,
//OutUserList,
init, init,
setUserList, setUserList,
getUserList, getUserList,
@ -471,12 +455,9 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
setChatId, setChatId,
sendMsg, sendMsg,
addText, addText,
addFile,
//uploadFile,
moreMsgList, moreMsgList,
refreshUserList, refreshUserList,
clearMsg, clearMsg,
//addUser,
handlerMessage, handlerMessage,
cannelFile, cannelFile,
accessFile accessFile

389
godo/localchat/file.go

@ -24,19 +24,10 @@
package localchat package localchat
import ( import (
"encoding/binary"
"encoding/json" "encoding/json"
"fmt"
"godo/libs" "godo/libs"
"io"
"log"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strconv"
"strings"
"sync"
"time" "time"
) )
@ -108,213 +99,223 @@ func HandlerAccessFile(w http.ResponseWriter, r *http.Request) {
msg.Time = time.Now() msg.Time = time.Now()
msg.Type = "fileAccessed" msg.Type = "fileAccessed"
SendToIP(msg) SendToIP(msg)
libs.SuccessMsg(w, nil, "请求文件发送成功") err = downloadFiles(msg)
}
func HandlerSendFile(msg UdpMessage) {
toIp := msg.IP
hostname, err := os.Hostname()
if err != nil { if err != nil {
log.Printf("HandleMessage error: %v", err) libs.ErrorMsg(w, "HandleMessage error")
return
}
msg.Hostname = hostname
msg.Time = time.Now()
msg.Type = "file"
basePath, err := libs.GetOsDir()
if err != nil {
log.Printf("GetOsDir error: %v", err)
return
}
paths, ok := msg.Message.([]string)
if !ok {
log.Printf("invalid message type")
return return
} }
for _, p := range paths { libs.SuccessMsg(w, msg.Message, "接收文件中")
filePath := filepath.Join(basePath, p)
// 处理单个文件或整个文件夹
if fileInfo, err := os.Stat(filePath); err == nil {
if fileInfo.IsDir() {
handleDirectory(filePath, toIp, msg)
} else {
handleFile(filePath, toIp, msg)
}
} else {
continue
}
}
msg.Type = "fileSended"
msg.Message = ""
msg.Time = time.Now()
SendToIP(msg)
} }
func downloadFiles(msg UdpMessage) error {
func handleFile(filePath string, toIp string, message UdpMessage) { return nil
// 打开文件 }
file, err := os.Open(filePath)
if err != nil {
log.Fatalf("Failed to open file: %v", err)
}
defer file.Close()
// 获取文件大小 // func HandlerSendFile(msg UdpMessage) {
fileInfo, err := file.Stat() // toIp := msg.IP
if err != nil { // hostname, err := os.Hostname()
log.Fatalf("Failed to get file info: %v", err) // if err != nil {
} // log.Printf("HandleMessage error: %v", err)
fileSize := fileInfo.Size() // return
// }
// msg.Hostname = hostname
// msg.Time = time.Now()
// msg.Type = "file"
// basePath, err := libs.GetOsDir()
// if err != nil {
// log.Printf("GetOsDir error: %v", err)
// return
// }
// paths, ok := msg.Message.([]string)
// if !ok {
// log.Printf("invalid message type")
// return
// }
// for _, p := range paths {
// filePath := filepath.Join(basePath, p)
// // 处理单个文件或整个文件夹
// if fileInfo, err := os.Stat(filePath); err == nil {
// if fileInfo.IsDir() {
// handleDirectory(filePath, toIp, msg)
// } else {
// handleFile(filePath, toIp, msg)
// }
// } else {
// continue
// }
// }
// msg.Type = "fileSended"
// msg.Message = ""
// msg.Time = time.Now()
// SendToIP(msg)
// }
// 计算需要发送的数据包数量 // func handleFile(filePath string, toIp string, message UdpMessage) {
numChunks := fileSize / GlobalfileSize // // 打开文件
if fileSize%GlobalfileSize != 0 { // file, err := os.Open(filePath)
numChunks++ // if err != nil {
} // log.Fatalf("Failed to open file: %v", err)
// 发送文件 // }
SendFile(file, int(numChunks), toIp, fileSize, message) // defer file.Close()
}
func handleDirectory(dirPath string, toIp string, message UdpMessage) { // // 获取文件大小
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { // fileInfo, err := file.Stat()
if err != nil { // if err != nil {
return err // log.Fatalf("Failed to get file info: %v", err)
} // }
if !info.IsDir() { // fileSize := fileInfo.Size()
handleFile(path, toIp, message)
}
return nil
})
if err != nil {
log.Fatalf("Failed to walk directory: %v", err)
}
}
func SendFile(file *os.File, numChunks int, toIp string, fSize int64, message UdpMessage) {
var wg sync.WaitGroup
// 逐块读取文件并发送 // // 计算需要发送的数据包数量
for i := 0; i < numChunks; i++ { // numChunks := fileSize / GlobalfileSize
wg.Add(1) // if fileSize%GlobalfileSize != 0 {
go func(index int) { // numChunks++
defer wg.Done() // }
idStr := message.Type + "@" + message.Hostname + "@" + filepath.Base(file.Name()) + "@" + fmt.Sprintf("%d", fSize) + "@" // // 发送文件
chunkData := make([]byte, GlobalfileSize) // SendFile(file, int(numChunks), toIp, fileSize, message)
n, err := file.Read(chunkData[:]) // }
if err != nil && err != io.EOF {
log.Fatalf("Failed to read file chunk: %v", err)
}
sendBinaryData(idStr, chunkData[:n], toIp)
fmt.Printf("发送文件块 %d 到 %s 成功\n", index, toIp)
}(i)
}
wg.Wait() // func handleDirectory(dirPath string, toIp string, message UdpMessage) {
} // err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
func sendBinaryData(idStr string, data []byte, toIp string) { // if err != nil {
port := "56780" // return err
addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", toIp, port)) // }
if err != nil { // if !info.IsDir() {
log.Fatalf("Failed to resolve UDP address: %v", err) // handleFile(path, toIp, message)
} // }
// return nil
// })
// if err != nil {
// log.Fatalf("Failed to walk directory: %v", err)
// }
// }
// func SendFile(file *os.File, numChunks int, toIp string, fSize int64, message UdpMessage) {
// var wg sync.WaitGroup
conn, err := net.DialUDP("udp4", nil, addr) // // 逐块读取文件并发送
if err != nil { // for i := 0; i < numChunks; i++ {
log.Fatalf("Failed to dial UDP address: %v", err) // wg.Add(1)
} // go func(index int) {
defer conn.Close() // defer wg.Done()
// idStr := message.Type + "@" + message.Hostname + "@" + filepath.Base(file.Name()) + "@" + fmt.Sprintf("%d", fSize) + "@"
// chunkData := make([]byte, GlobalfileSize)
// n, err := file.Read(chunkData[:])
// if err != nil && err != io.EOF {
// log.Fatalf("Failed to read file chunk: %v", err)
// }
// sendBinaryData(idStr, chunkData[:n], toIp)
// fmt.Printf("发送文件块 %d 到 %s 成功\n", index, toIp)
// }(i)
// }
// 添加长度前缀 // wg.Wait()
identifier := []byte(idStr) // }
lengthPrefix := make([]byte, 4) // func sendBinaryData(idStr string, data []byte, toIp string) {
binary.BigEndian.PutUint32(lengthPrefix, uint32(len(data))) // 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)
_, err = conn.Write(append(append(identifier, lengthPrefix...), data...)) // if err != nil {
if err != nil { // log.Fatalf("Failed to dial UDP address: %v", err)
log.Printf("Failed to write data: %v", err) // }
} // defer conn.Close()
}
var fileLocks = make(map[string]*sync.Mutex) // // 添加长度前缀
// identifier := []byte(idStr)
// lengthPrefix := make([]byte, 4)
// binary.BigEndian.PutUint32(lengthPrefix, uint32(len(data)))
func ReceiveFiles(parts []string) (string, error) { // // 发送长度前缀和数据
fileName := parts[2] // _, err = conn.Write(append(append(identifier, lengthPrefix...), data...))
fileSize, err := strconv.ParseInt(parts[3], 10, 64) // if err != nil {
if err != nil { // log.Printf("Failed to write data: %v", err)
return "", fmt.Errorf("failed to parse file size: %v", err) // }
} // }
// 创建接收文件的目录 // var fileLocks = make(map[string]*sync.Mutex)
baseDir, err := libs.GetOsDir()
if err != nil {
log.Printf("Failed to get OS directory: %v", err)
return "", fmt.Errorf("failed to get OS directory")
}
resPath := filepath.Join("C", "Users", "Reciv", time.Now().Format("2006-01-02")) // func ReceiveFiles(parts []string) (string, error) {
receiveDir := filepath.Join(baseDir, resPath) // fileName := parts[2]
if !libs.PathExists(receiveDir) { // fileSize, err := strconv.ParseInt(parts[3], 10, 64)
err := os.MkdirAll(receiveDir, 0755) // if err != nil {
if err != nil { // return "", fmt.Errorf("failed to parse file size: %v", err)
log.Printf("Failed to create receive directory: %v", err) // }
return "", fmt.Errorf("failed to create receive directory")
}
}
// 确定文件路径 // // 创建接收文件的目录
filePath := filepath.Join(receiveDir, fileName) // baseDir, err := libs.GetOsDir()
// if err != nil {
// log.Printf("Failed to get OS directory: %v", err)
// return "", fmt.Errorf("failed to get OS directory")
// }
// 如果文件不存在,则创建新文件 // resPath := filepath.Join("C", "Users", "Reciv", time.Now().Format("2006-01-02"))
if _, err := os.Stat(filePath); os.IsNotExist(err) { // receiveDir := filepath.Join(baseDir, resPath)
file, err := os.Create(filePath) // if !libs.PathExists(receiveDir) {
if err != nil { // err := os.MkdirAll(receiveDir, 0755)
log.Printf("Failed to create file: %v", err) // if err != nil {
return "", fmt.Errorf("failed to create file") // log.Printf("Failed to create receive directory: %v", err)
} // return "", fmt.Errorf("failed to create receive directory")
defer file.Close() // }
} // }
// 锁定文件 // // 确定文件路径
lock, ok := fileLocks[fileName] // filePath := filepath.Join(receiveDir, fileName)
if !ok {
lock = &sync.Mutex{}
fileLocks[fileName] = lock
}
lock.Lock()
defer lock.Unlock()
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) // // 如果文件不存在,则创建新文件
if err != nil { // if _, err := os.Stat(filePath); os.IsNotExist(err) {
log.Printf("Failed to open file: %v", err) // file, err := os.Create(filePath)
return "", fmt.Errorf("failed to open file") // if err != nil {
} // log.Printf("Failed to create file: %v", err)
defer file.Close() // return "", fmt.Errorf("failed to create file")
// }
// defer file.Close()
// }
// 提取实际数据 // // 锁定文件
data := strings.Join(parts[4:], "") // lock, ok := fileLocks[fileName]
if len(data) < 1 { // if !ok {
return "", fmt.Errorf("empty data") // lock = &sync.Mutex{}
} // fileLocks[fileName] = lock
// }
// lock.Lock()
// defer lock.Unlock()
n, err := file.Write([]byte(data)) // file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { // if err != nil {
log.Printf("Failed to write data to file: %v", err) // log.Printf("Failed to open file: %v", err)
return "", fmt.Errorf("failed to write data to file") // return "", fmt.Errorf("failed to open file")
} // }
if n != len(data) { // defer file.Close()
log.Printf("Incomplete write: wrote %d bytes, expected %d bytes", n, len(data))
return "", fmt.Errorf("incomplete write")
}
fileInfo, err := os.Stat(filePath) // // 提取实际数据
if err != nil { // data := strings.Join(parts[4:], "")
log.Printf("Failed to stat file: %v", err) // if len(data) < 1 {
return "", fmt.Errorf("failed to stat file") // return "", fmt.Errorf("empty data")
} // }
if fileInfo.Size() == fileSize { // n, err := file.Write([]byte(data))
fmt.Println("文件接收完成且大小一致") // if err != nil {
return filePath, nil // log.Printf("Failed to write data to file: %v", err)
} else { // return "", fmt.Errorf("failed to write data to file")
fmt.Printf("文件大小不一致,发送大小为%d,接收大小为%d\n", fileSize, fileInfo.Size()) // }
return "", fmt.Errorf("file size mismatch") // if n != len(data) {
} // log.Printf("Incomplete write: wrote %d bytes, expected %d bytes", n, len(data))
} // return "", fmt.Errorf("incomplete write")
// }
// 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() == fileSize {
// fmt.Println("文件接收完成且大小一致")
// return filePath, nil
// } else {
// fmt.Printf("文件大小不一致,发送大小为%d,接收大小为%d\n", fileSize, fileInfo.Size())
// return "", fmt.Errorf("file size mismatch")
// }
// }

8
godo/localchat/server.go

@ -117,10 +117,10 @@ func UdpServer() {
// ReceiveFile(udpMsg) // ReceiveFile(udpMsg)
// continue // continue
// } // }
if udpMsg.Type == "fileAccessed" { // if udpMsg.Type == "fileAccessed" {
HandlerSendFile(udpMsg) // HandlerSendFile(udpMsg)
continue // continue
} // }
if udpMsg.Type == "image" { if udpMsg.Type == "image" {
filePath, err := ReceiveImg(udpMsg) filePath, err := ReceiveImg(udpMsg)
if err != nil { if err != nil {

Loading…
Cancel
Save