Go: IM系统接入ws进行消息发送以及群聊功能 (5)
概述
- 在即时通讯(IM)系统中,实现多媒体消息(如文本、表情包、拍照、图片、音频、视频)的实时传输是一项核心功能
- 随着HTML5和WebSocket技术的发展,现代Web应用能够支持更高效、更实时的通信方式
- 本文将详细探讨如何使用Go语言结合WebSocket技术,在IM系统中实现多媒体消息的发送和接收
基于MVC的目录设计
im-project ├── go.mod ├── main.go 主程序 ├── ctrl 控制器层 │ └── chat.go ├── views 模板层 │ └── chat │ ├── foot.html │ └── x.html
主程序
main.go 核心代码
(图片来源网络,侵删)package main import ( "net/http" "im-project/ctrl" ) func main() { // 1. 绑定请求和处理函数 http.HandleFunc("/chat", ctrl.Chat) http.HandleFunc("/attach/upload", ctrl.Upload) // 2. 指定目录的静态文件 http.Handle("/asset/",http.FileServer(http.Dir("."))) http.Handle("/mnt/",http.FileServer(http.Dir("."))) // 3. 启动 http.ListenAndServe(":80",nil) }控制器
ctrl/attach.go
package ctrl import ( "net/http" "im-project/util" "os" "strings" "fmt" "time" "math/rand" "io" "github.com/aliyun/aliyun-oss-go-sdk/oss" ) func init(){ os.MkdirAll("./mnt",os.ModePerm) } func Upload(w http.ResponseWriter, r *http.Request){ //UploadLocal(w,r) UploadOss(w,r) } // 1.存储位置 ./mnt,需要确保已经创建好 // 2.url格式 /mnt/xxxx.png 需要确保网络能访问/mnt/ func UploadLocal(writer http.ResponseWriter, request * http.Request) { // 获得上传的源文件 srcfile,head,err:=request.FormFile("file") if err!=nil{ util.RespFail(writer,err.Error()) } // 创建一个新文件 suffix := ".png" // 如果前端文件名称包含后缀 xx.xx.png ofilename := head.Filename tmp := strings.Split(ofilename,".") if len(tmp)>1 { suffix = "."+tmp[len(tmp)-1] } // 如果前端指定filetype // formdata.append("filetype",".png") filetype := request.FormValue("filetype") if len(filetype)>0 { suffix = filetype } // time.Now().Unix() filename := fmt.Sprintf("%d%04d%s", time.Now().Unix(), rand.Int31(), suffix) dstfile,err:= os.Create("./mnt/"+filename) if err!=nil { util.RespFail(writer,err.Error()) return } // todo 将源文件内容copy到新文件 _,err = io.Copy(dstfile,srcfile) if err!=nil{ util.RespFail(writer,err.Error()) return } // 将新文件路径转换成url地址 url := "/mnt/"+filename // 响应到前端 util.RespOk(writer,url,"") } // 即将删掉,定期更新 const ( AccessKeyId="5p2RZ******nMuQw9" // 填入自己的 key AccessKeySecret="bsNmjU8Au08*****S5XIFAkK" // 填入自己的secret EndPoint="oss-cn-shenzhen.aliyuncs.com" Bucket="winliondev" ) // 权限设置为公共读状态 // 需要安装 func UploadOss(writer http.ResponseWriter, request * http.Request) { // 获得上传的文件 srcfile,head,err := request.FormFile("file") if err!=nil { util.RespFail(writer,err.Error()) return } // 获得文件后缀.png/.mp3 suffix := ".png" //如果前端文件名称包含后缀 xx.xx.png ofilename := head.Filename tmp := strings.Split(ofilename,".") if len(tmp)>1 { suffix = "."+tmp[len(tmp)-1] } // 如果前端指定filetype // formdata.append("filetype",".png") filetype := request.FormValue("filetype") if len(filetype)>0{ suffix = filetype } // 初始化ossclient client,err:=oss.New(EndPoint,AccessKeyId,AccessKeySecret) if err!=nil{ util.RespFail(writer,err.Error()) return } // todo 获得bucket bucket,err := client.Bucket(Bucket) if err!=nil{ util.RespFail(writer,err.Error()) return } // 设置文件名称 // time.Now().Unix() filename := fmt.Sprintf("mnt/%d%04d%s", time.Now().Unix(), rand.Int31(), suffix) // 通过bucket上传 err = bucket.PutObject(filename, srcfile) if err!=nil { util.RespFail(writer,err.Error()) return } // 获得url地址 url := "http://"+Bucket+"."+EndPoint+"/"+filename // 响应到前端 util.RespOk(writer,url,"") }ctrl/chat.go
package ctrl import ( "net/http" "github.com/gorilla/websocket" "gopkg.in/fatih/set.v0" "sync" "strconv" "log" "fmt" "encoding/json" ) const ( CMD_SINGLE_MSG = 10 CMD_ROOM_MSG = 11 CMD_HEART = 0 ) type Message struct { Id int64 `json:"id,omitempty" form:"id"` //消息ID Userid int64 `json:"userid,omitempty" form:"userid"` //谁发的 Cmd int `json:"cmd,omitempty" form:"cmd"` //群聊还是私聊 Dstid int64 `json:"dstid,omitempty" form:"dstid"`//对端用户ID/群ID Media int `json:"media,omitempty" form:"media"` //消息按照什么样式展示 Content string `json:"content,omitempty" form:"content"` //消息的内容 Pic string `json:"pic,omitempty" form:"pic"` //预览图片 Url string `json:"url,omitempty" form:"url"` //服务的URL Memo string `json:"memo,omitempty" form:"memo"` //简单描述 Amount int `json:"amount,omitempty" form:"amount"` //其他和数字相关的 } /** 消息发送结构体 1、MEDIA_TYPE_TEXT {id:1,userid:2,dstid:3,cmd:10,media:1,content:"hello"} 2、MEDIA_TYPE_News {id:1,userid:2,dstid:3,cmd:10,media:2,content:"标题",pic:"http://www.baidu.com/a/log,jpg",url:"http://www.a,com/dsturl","memo":"这是描述"} 3、MEDIA_TYPE_VOICE,amount单位秒 {id:1,userid:2,dstid:3,cmd:10,media:3,url:"http://www.a,com/dsturl.mp3",anount:40} 4、MEDIA_TYPE_IMG {id:1,userid:2,dstid:3,cmd:10,media:4,url:"http://www.baidu.com/a/log,jpg"} 5、MEDIA_TYPE_REDPACKAGR //红包amount 单位分 {id:1,userid:2,dstid:3,cmd:10,media:5,url:"http://www.baidu.com/a/b/c/redpackageaddress?id=100000","amount":300,"memo":"恭喜发财"} 6、MEDIA_TYPE_EMOJ 6 {id:1,userid:2,dstid:3,cmd:10,media:6,"content":"cry"} 7、MEDIA_TYPE_Link 6 {id:1,userid:2,dstid:3,cmd:10,media:7,"url":"http://www.a,com/dsturl.html"} 7、MEDIA_TYPE_Link 6 {id:1,userid:2,dstid:3,cmd:10,media:7,"url":"http://www.a,com/dsturl.html"} 8、MEDIA_TYPE_VIDEO 8 {id:1,userid:2,dstid:3,cmd:10,media:8,pic:"http://www.baidu.com/a/log,jpg",url:"http://www.a,com/a.mp4"} 9、MEDIA_TYPE_CONTACT 9 {id:1,userid:2,dstid:3,cmd:10,media:9,"content":"10086","pic":"http://www.baidu.com/a/avatar,jpg","memo":"胡大力"} */ // 本核心在于形成userid和Node的映射关系 type Node struct { Conn *websocket.Conn //并行转串行, DataQueue chan []byte GroupSets set.Interface } // 映射关系表 var clientMap map[int64]*Node = make(map[int64]*Node,0) // 读写锁 var rwlocker sync.RWMutex // ws://127.0.0.1/chat?id=1&token=xxxx func Chat(writer http.ResponseWriter, request *http.Request) { // 检验接入是否合法 // checkToken(userId int64, token string) query := request.URL.Query() id := query.Get("id") token := query.Get("token") userId ,_ := strconv.ParseInt(id,10,64) isvalida := checkToken(userId,token) // 如果 isvalida = true // isvalida = false conn,err :=(&websocket.Upgrader { CheckOrigin: func(r *http.Request) bool { return isvalida }, }).Upgrade(writer,request,nil) if err!=nil { log.Println(err.Error()) return } // 获得conn node := &Node { Conn:conn, DataQueue:make(chan []byte,50), GroupSets:set.New(set.ThreadSafe), } // 获取用户全部群Id comIds := contactService.SearchComunityIds(userId) for _,v:=range comIds { node.GroupSets.Add(v) } // userid和node形成绑定关系 rwlocker.Lock() clientMap[userId] = node rwlocker.Unlock() // 完成发送逻辑, con go sendproc(node) // 完成接收逻辑 go recvproc(node) sendMsg(userId,[]byte("hello,world!")) } //发送协程 func sendproc(node *Node) { for { select { case data:= log.Println(err.Error()) return } } } } // 添加新的群ID到用户的groupset中 func AddGroupId(userId,gid int64) { // 取得node rwlocker.Lock() node,ok := clientMap[userId] if ok { node.GroupSets.Add(gid) } // clientMap[userId] = node rwlocker.Unlock() // 添加gid到set } // 接收协程 func recvproc(node *Node) { for { _,data,err := node.Conn.ReadMessage() if err!=nil { log.Println(err.Error()) return } // 对data进一步处理 dispatch(data) fmt.Printf("recv
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!
