WebSocket多文件传输异常:代码问题排查及正确实现咨询
问题描述
我想通过WebSocket向服务器发送多个文件,目前写了前端和后端代码但无法正常运行。我觉得问题出在前端sendFile函数的缓冲区读取环节,但解决不了。请问该怎么实现多文件的独立传输?
前端代码
<html lang="en"> <head> <link rel="shortcut icon" href="#" /> <script type="text/javascript"> window.onload = function () { var conn; var server_msg = document.getElementById("server_msg"); function sendFile(i) { indx = i.toString(); var file = document.getElementById("image"+indx).files[0]; if (!file) { conn.send("none"); } else { conn.send(indx+file.name); var reader = new FileReader(); var rawData = new ArrayBuffer(); reader.loadend = function() { } reader.onload = function(e) { rawData = e.target.result; conn.send(rawData); } reader.readAsArrayBuffer(file); } } document.getElementById("form").onsubmit = function () { if (!conn) { return false; } for (let i = 1; i < 4; i++) { sendFile(i); } return false; }; if (window["WebSocket"]) { conn = new WebSocket("ws://" + document.location.host + "/ws"); conn.binaryType = "arraybuffer"; conn.onopen = function (evt) { server_msg.innerHTML = "<b>Connection opened.</b>"; return false; }; conn.onmessage = function (evt) { server_msg.innerHTML = evt.data; return false; }; conn.onclose = function (evt) { server_msg.innerHTML = "<b>Connection closed.</b>"; return false; }; conn.onerror = function (evt) { server_msg.innerHTML = "<b>Error happened!!!</b>" + evt.value; return false; } } else { server_msg.innerHTML = "<b>Your browser does not support WebSockets.</b>"; } }; </script> </head> <body> <div id="server_msg"></div> <form id="form" enctype="multipart/form-data"> <input type="file" id="image1" name="image1"> <br> <input type="file" id="image2" name="image2"> <br> <input type="file" id="image3" name="image3"> <br> <input type="submit" value="Send" /> </form> </body> </html>
后端代码
app.Get("/ws", websocket.New(func(c *websocket.Conn) { defer c.Close() for ind := 1; ind < 4; ind++ { // step-1: check if filename is null _, msg, err := c.ReadMessage() if err != nil { log.Println("Producer: read error 1 - ", err) return } filename := string(msg) log.Printf("Producer: recieved filename - %s", filename) if filename != "none" { // step-2: read image bytes _, img_bytes, err := c.ReadMessage() if err != nil { log.Println("Producer: read error 2 - ", err) return } log.Println("Producer: recieved raw data") // step-3: save image fp, err := os.Create(filename) if err != nil { log.Fatal(err) } _, err2 := fp.Write(img_bytes) if err2 != nil { log.Fatal(err2) } fp.Close() } } }))
问题分析与解决
核心问题是WebSocket发送异步,循环调用sendFile会导致消息顺序混乱:你同时触发3个文件的读取和发送,后端按固定顺序读取,但前端文件读取完成时间不确定,导致文件名和内容消息交叉到达,后端无法正确匹配。
前端修改方案
将sendFile改为异步函数,通过Promise控制发送顺序,确保一个文件的文件名和内容发送完成后,再处理下一个文件:
<html lang="en"> <head> <link rel="shortcut icon" href="#" /> <script type="text/javascript"> window.onload = function () { var conn; var server_msg = document.getElementById("server_msg"); // 异步发送单个文件,返回Promise确保顺序 async function sendFile(i) { const indx = i.toString(); const fileInput = document.getElementById("image"+indx); const file = fileInput.files[0]; if (!file) { await new Promise(resolve => { conn.send("none"); resolve(); }); } else { // 先发送文件名 await new Promise(resolve => { conn.send(indx+file.name); resolve(); }); // 读取文件并发送内容 const rawData = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = function(e) { resolve(e.target.result); }; reader.onerror = reject; reader.readAsArrayBuffer(file); }); await new Promise(resolve => { conn.send(rawData); resolve(); }); } } document.getElementById("form").onsubmit = async function () { if (!conn) { return false; } // 串行发送,确保顺序 for (let i = 1; i < 4; i++) { await sendFile(i); } server_msg.innerHTML += "<br>所有文件已发送"; return false; }; if (window["WebSocket"]) { conn = new WebSocket("ws://" + document.location.host + "/ws"); conn.binaryType = "arraybuffer"; conn.onopen = function (evt) { server_msg.innerHTML = "<b>Connection opened.</b>"; }; conn.onmessage = function (evt) { server_msg.innerHTML += "<br>" + evt.data; }; conn.onclose = function (evt) { server_msg.innerHTML = "<b>Connection closed.</b>"; }; conn.onerror = function (evt) { server_msg.innerHTML = "<b>Error happened!!!</b>" + evt.message; } } else { server_msg.innerHTML = "<b>Your browser does not support WebSockets.</b>"; } }; </script> </head> <body> <div id="server_msg"></div> <form id="form" enctype="multipart/form-data"> <input type="file" id="image1" name="image1"> <br> <input type="file" id="image2" name="image2"> <br> <input type="file" id="image3" name="image3"> <br> <input type="submit" value="Send" /> </form> </body> </html>
后端优化方案
将固定循环改为持续读取,添加错误反馈和结束标记,提升灵活性:
app.Get("/ws", websocket.New(func(c *websocket.Conn) { defer c.Close() for { // 读取文件名或结束标记 _, msg, err := c.ReadMessage() if err != nil { if websocket.IsCloseError(err, websocket.CloseNormalClosure) { log.Println("客户端正常关闭连接") } else { log.Println("Producer: read error 1 - ", err) } return } filename := string(msg) // 约定"end"为结束标记,收到后停止接收 if filename == "end" { log.Println("收到结束标记,停止接收文件") c.WriteMessage(websocket.TextMessage, []byte("所有文件接收完成")) return } log.Printf("Producer: recieved filename - %s", filename) if filename != "none" { // 读取文件内容 _, img_bytes, err := c.ReadMessage() if err != nil { log.Println("Producer: read error 2 - ", err) return } log.Println("Producer: recieved raw data") // 保存文件,添加defer确保文件关闭 fp, err := os.Create(filename) if err != nil { log.Printf("创建文件失败: %v", err) c.WriteMessage(websocket.TextMessage, []byte("创建文件失败: "+filename)) continue } defer fp.Close() _, err2 := fp.Write(img_bytes) if err2 != nil { log.Printf("写入文件失败: %v", err2) c.WriteMessage(websocket.TextMessage, []byte("写入文件失败: "+filename)) continue } log.Printf("文件保存成功: %s", filename) c.WriteMessage(websocket.TextMessage, []byte("文件接收完成: "+filename)) } } }))
关键说明
- 顺序保证:通过
async/await实现串行发送,确保每个文件的文件名和内容按顺序到达后端。 - 错误反馈:前后端添加了错误提示,方便调试问题。
- 灵活性:后端改为循环读取直到收到结束标记,不再依赖固定的文件数量。
内容的提问来源于stack exchange,提问作者Eziz Durdyyev
相关产品推荐
相关产品推荐

