GO语言实现同步传输系统:局域网内手机和电脑互传文件互发消息

  • 项目总览:
  • 一、项目功能展示
    • 1.用手机传输文件到电脑
    • 2.用手机传输图片到电脑
    • 3.用电脑传输文字到手机
    • 4.服务器显示情况,端口信息:
  • 二、总体规划
    • 1.需求分析
    • 2.项目构思图
    • 3.项目结构图
  • 三、main函数部分
  • 四、server.go文件
  • 五、controller文件夹,对于server中的5个函数,用于对接前端接口实现相应的功能。
    • 1.AddressesController函数:获取局域网中的IP地址然后传给json
      • 思路:
      • 代码:
    • 2.FilesController函数:实现上传文件功能
      • 思路:
      • 代码:
    • 3. QrcodesController函数:将链接转为二维码
      • 思路:
      • 代码:
    • 4.TextsController函数:上传文本
      • 思路:
      • 代码:
    • 5.UploadsController函数:实现下载功能
      • 思路:
      • 代码:
  • 六、ws部分,用于实现websocket功能
    • 1.什么是websocket:
    • 2.代码块部分(参考github.com/gorilla/websocket所给出的案例):
      • 1.client.go:
      • 2.http_controller.go:
      • 3.hub.go:

项目总览:

1.开发语言:GO语言
2.IDE:Goland
3.开发用时:一周
4.使用到的第三方库:lorca——用于生成UI窗体;gin——提供服务器接口;gorilla——websocket服务;qrcode——链接转二维码。
5.源码已上传到我的GitHub,链接:https://github.com/2394799692/transmit-doc 或点此跳转
6.鸣谢:感谢方应杭老师分享的开源项目,使我有了这次练习的机会,以及在写代码与运行调试中出现一些问题老师在微信及时的解答和远程操控帮助解决,十分感谢。
7.前端代码也打包放在GitHub中,因为本人主要练习后端,故前端方面不介绍具体代码,网页拿来直接使用就行。


以下是本篇文章正文内容,欢迎朋友们进行指正,一起探讨,共同进步。——来自考研路上的lwj。QQ:2394799692

一、项目功能展示

1.用手机传输文件到电脑

2.用手机传输图片到电脑

3.用电脑传输文字到手机

4.服务器显示情况,端口信息:

二、总体规划

1.需求分析

不开微信,蓝牙,不注册账号,只扫个二维码即可完成数据传输

2.项目构思图

3.项目结构图

三、main函数部分

package mainimport ("os""os/exec""os/signal""synk/config""synk/server"
)func main() {go server.Run()                 //启动gin协程cmd := startBrowser()           //启动ui界面,打开ChromechSignal := listenToInterrupt() //监听关闭信号<-chSignal                      //从chan中读出中断信号cmd.Process.Kill()              //关闭谷歌浏览器进程
}func startBrowser() *exec.Cmd {// 先写死路径,后面再照着 lorca 改chromePath := "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"cmd := exec.Command(chromePath, "--app=http://127.0.0.1:"+config.GetPort()+"/static/index.html")cmd.Start()return cmd
}func listenToInterrupt() chan os.Signal {chSignal := make(chan os.Signal, 1)   //接收系统信号signal.Notify(chSignal, os.Interrupt) //用户按下ctrl+c,就发送系统信号return chSignal
}

四、server.go文件

package serverimport ("embed""github.com/gin-gonic/gin""io/fs""log""net/http""strings""synk/config"c "synk/server/controller""synk/server/ws"
)//go:embed frontend/dist/*
var FS embed.FS//可以在Go语言应用程序中包含任何文件、目录的内容
//也就是说我们可以把文件以及目录中的内容都打包到生成的Go语言应用程序中了,
//部署的时候,直接扔一个二进制文件就可以了,不用再包含一些静态文件了,因为它们已经被打包到生成的应用程序中了。
//静态文件的Web托管,案例:
//var static embed.FS
//func main() {
//  http.ListenAndServe(":8080", http.FileServer(http.FS(static)))
//}func Run() {hub := ws.NewHub()go hub.Run()               //启动websocket服务gin.SetMode(gin.DebugMode) //gin开发模式//使用不同运行模式方便应对不同场景,比如debug模式下,output format不同,logger也不同。router := gin.Default() //创建新的引擎,实现服务器监听状态//r := gin.Default() 创建带有默认中间件的路由//r :=gin.new()      创建带有没有中间件的路由//中间间:将具体业务和底层逻辑解耦的组件。//需要利用服务的人(前端写业务的),不需要知道底层逻辑(提供服务的)的具体实现,只要拿着中间件结果来用就好了。staticFiles, _ := fs.Sub(FS, "frontend/dist")          //把打包好的静态文件变成一个结构化的目录router.POST("/api/v1/files", c.FilesController)        //上传文件router.GET("/api/v1/qrcodes", c.QrcodesController)     //将局域网ip变为二维码router.GET("/uploads/:path", c.UploadsController)      //下载接口router.GET("/api/v1/addresses", c.AddressesController) //获取当前局域网iprouter.POST("/api/v1/texts", c.TextsController)        //上传文本router.GET("/ws", func(c *gin.Context) {               //上下文是一个结构体ws.HttpController(c, hub) //websocak,实现手机穿文件到电脑,将http请求升级为websocket})router.StaticFS("/static", http.FS(staticFiles)) //访问本地文件,加载前端页面router.NoRoute(func(c *gin.Context) {            //设置默认路由,防止文件路径出错,如果出错返回404如果该目录下没有文件则显示默认页面indexpath := c.Request.URL.Pathif strings.HasPrefix(path, "/static/") {reader, err := staticFiles.Open("index.html")if err != nil {log.Fatal(err)}defer reader.Close() //defer表示go会在恰当时间关闭(垃圾回收机制)stat, err := reader.Stat()if err != nil {log.Fatal(err)}c.DataFromReader(http.StatusOK, stat.Size(), "text/html;charset=utf-8", reader, nil)} else {c.Status(http.StatusNotFound) //返回状态码404}})router.Run(":" + config.GetPort()) //监听端口
}

五、controller文件夹,对于server中的5个函数,用于对接前端接口实现相应的功能。

1.AddressesController函数:获取局域网中的IP地址然后传给json

思路:

//1.获取电脑在各个局域网的IP地址
//2.转为json写入HTTP响应

代码:

package controllerimport ("github.com/gin-gonic/gin""net""net/http"
)//思路:
//1.获取电脑在各个局域网的IP地址
//2.转为json写入HTTP响应func AddressesController(c *gin.Context) {addrs, _ := net.InterfaceAddrs() //获取当前电脑的所有ip地址var result []stringfor _, address := range addrs { //遍历所有ip地址// check the address type and if it is not a loopback(回环) the display itif ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {if ipnet.IP.To4() != nil { //将地址存储到result切片中result = append(result, ipnet.IP.String())}}}c.JSON(http.StatusOK, gin.H{"addresses": result}) //作为一个json返回给前端
}

2.FilesController函数:实现上传文件功能

思路:

//1.获取go执行文件所在目录
//2.在该目录创建uploads目录
//3.将文件保存为另一个文件
//4.返回后者的下载路径

代码:

package controllerimport ("log""net/http""os""path""path/filepath""github.com/gin-gonic/gin""github.com/google/uuid"
)//思路:
//1.获取go执行文件所在目录
//2.在该目录创建uploads目录
//3.将文件保存为另一个文件
//4.返回后者的下载路径func FilesController(c *gin.Context) {file, err := c.FormFile("raw") //读取用户上传的文件if err != nil {log.Fatal(err)}exe, err := os.Executable() //输出一个临时文件的路径if err != nil {log.Fatal(err)}dir := filepath.Dir(exe) //用于返回指定路径中除最后一个元素以外的所有元素。if err != nil {log.Fatal(err)}filename := uuid.New().String() //创建uploads文件uploads := filepath.Join(dir, "uploads")err = os.MkdirAll(uploads, os.ModePerm)if err != nil {log.Fatal(err)}fullpath := path.Join("uploads", filename+filepath.Ext(file.Filename)) //获取本地文件路径fileErr := c.SaveUploadedFile(file, filepath.Join(dir, fullpath))      //存储用户上传的文件if fileErr != nil {log.Fatal(fileErr)}c.JSON(http.StatusOK, gin.H{"url": "/" + fullpath})
}

3. QrcodesController函数:将链接转为二维码

思路:

//1.获取文本内容
//2.将文本转为图片(用qrcode库)
//3.将图片写入HTTP响应

代码:

package controllerimport ("log""net/http""github.com/gin-gonic/gin""github.com/skip2/go-qrcode"
)//思路:
//1.获取文本内容
//2.将文本转为图片(用qrcode库)
//3.将图片写入HTTP响应func QrcodesController(c *gin.Context) {if content := c.Query("content"); content != "" {png, err := qrcode.Encode(content, qrcode.Medium, 256) //把文本编程pngif err != nil {log.Fatal(err)}c.Data(http.StatusOK, "image/png", png) //把图片传给前端,用于展示} else {c.Status(http.StatusBadRequest) //否则返回一个错误}
}

4.TextsController函数:上传文本

思路:

//1.获取go执行文件所在目录
//2.在该目录创建uploads目录、
//3.将文本保存为一个文件
//4.返回该文件的下载路径

代码:

package controllerimport ("io/ioutil""log""net/http""os""path""path/filepath""github.com/gin-gonic/gin""github.com/google/uuid"
)//思路:
//1.获取go执行文件所在目录
//2.在该目录创建uploads目录、
//3.将文本保存为一个文件
//4.返回该文件的下载路径func TextsController(c *gin.Context) { //上传文本函数实现var json struct { //声明json,用户上传的jsonRaw string `json:"raw"`}if err := c.ShouldBindJSON(&json); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})} else {exe, err := os.Executable() // 获取当前执行文件的路径if err != nil {log.Fatal(err)}dir := filepath.Dir(exe) // 获取当前执行文件的目录if err != nil {log.Fatal(err)}filename := uuid.New().String()          // 生成一个文件名uploads := filepath.Join(dir, "uploads") // 拼接 uploads 的绝对路径err = os.MkdirAll(uploads, os.ModePerm)  // 创建 uploads 目录if err != nil {log.Fatal(err)}fullpath := path.Join("uploads", filename+".txt")                            // 拼接文件的绝对路径(不含 exe 所在目录)err = ioutil.WriteFile(filepath.Join(dir, fullpath), []byte(json.Raw), 0644) // 将 json.Raw 写入文件if err != nil {log.Fatal(err)}c.JSON(http.StatusOK, gin.H{"url": "/" + fullpath}) // 返回文件的绝对路径(不含 exe 所在目录)}}

5.UploadsController函数:实现下载功能

思路:

//1.将网络路径:path变成本地绝对路径
//2.读取本地文件,写到HTTP响应中

代码:

package controllerimport ("log""net/http""os""path/filepath""github.com/gin-gonic/gin"
)//思路:
//1.将网络路径:path变成本地绝对路径
//2.读取本地文件,写到HTTP响应中func getUploadsDir() (uploads string) {exe, err := os.Executable()if err != nil {log.Fatal(err)}dir := filepath.Dir(exe)uploads = filepath.Join(dir, "uploads")return
}func UploadsController(c *gin.Context) {if path := c.Param("path"); path != "" { //获取路径target := filepath.Join(getUploadsDir(), path)c.Header("Content-Description", "File Transfer")c.Header("Content-Transfer-Encoding", "binary")c.Header("Content-Disposition", "attachment; filename="+path)c.Header("Content-Type", "application/octet-stream")c.File(target) //给前端发送一个文件,各种类型} else {c.Status(http.StatusNotFound)}
}

六、ws部分,用于实现websocket功能

1.什么是websocket:

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

2.代码块部分(参考github.com/gorilla/websocket所给出的案例):

1.client.go:

package wsimport ("bytes""log""time""github.com/gorilla/websocket"
)const (writeWait      = 10 * time.SecondpongWait       = 60 * time.SecondpingPeriod     = (pongWait * 9) / 10maxMessageSize = 512
)var (newline = []byte{'\n'}space   = []byte{' '}
)var upgrader = websocket.Upgrader{ //升级websocket读和写的缓存大小ReadBufferSize:  1024,WriteBufferSize: 1024,
}// Client is a middleman between the websocket connection and the hub.
type Client struct {hub  *Hubconn *websocket.Connsend chan []byte
}func (c *Client) readPump() {defer func() {c.hub.unregister <- cc.conn.Close()}()c.conn.SetReadLimit(maxMessageSize)c.conn.SetReadDeadline(time.Now().Add(pongWait))c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })for {_, message, err := c.conn.ReadMessage()if err != nil {if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {log.Printf("error: %v", err)}break}message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))c.hub.broadcast <- message}
}func (c *Client) writePump() {ticker := time.NewTicker(pingPeriod)defer func() {ticker.Stop()c.conn.Close()}()for {select {case message, ok := <-c.send:c.conn.SetWriteDeadline(time.Now().Add(writeWait))if !ok {c.conn.WriteMessage(websocket.CloseMessage, []byte{})return}w, err := c.conn.NextWriter(websocket.TextMessage)if err != nil {return}w.Write(message)// Add queued chat messages to the current websocket message.n := len(c.send)for i := 0; i < n; i++ {w.Write(newline)w.Write(<-c.send)}if err := w.Close(); err != nil {return}case <-ticker.C:c.conn.SetWriteDeadline(time.Now().Add(writeWait))if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {return}}}
}

2.http_controller.go:

package wsimport ("log""net/http""github.com/gin-gonic/gin""github.com/gorilla/websocket"
)var wsupgrader = websocket.Upgrader{ReadBufferSize:  1024,WriteBufferSize: 1024,CheckOrigin: func(r *http.Request) bool {return true},
}func wshandler(hub *Hub, w http.ResponseWriter, r *http.Request) {conn, err := wsupgrader.Upgrade(w, r, nil)if err != nil {log.Println(err)return}client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}client.hub.register <- clientgo client.writePump()go client.readPump()
}
func HttpController(c *gin.Context, hub *Hub) {wshandler(hub, c.Writer, c.Request)
}

3.hub.go:

package wsimport ("sync"
)type Hub struct {clients    map[*Client]boolbroadcast  chan []byte  //广播事件register   chan *Client //监听事件unregister chan *Client //取消监听
}func NewHub() *Hub {return &Hub{broadcast:  make(chan []byte),register:   make(chan *Client),unregister: make(chan *Client),clients:    make(map[*Client]bool),}
}var once sync.Once
var singleton *Hubfunc (h *Hub) Run() {for {select {case client := <-h.register: //当有人注册后h.clients[client] = truecase client := <-h.unregister:if _, ok := h.clients[client]; ok {delete(h.clients, client)close(client.send)}case message := <-h.broadcast:for client := range h.clients {select {case client.send <- message:default:close(client.send)delete(h.clients, client)}}}}
}

本章完结,这是一个很好的入门练手项目,如果有任何疑问,欢迎评论区留言或私信探讨。

【GO语言】实现同步传输系统:局域网内手机和电脑互传文件互发消息。go语言练手项目相关推荐

  1. 同一局域网内手机访问电脑本地localhost网页

    1. 手机和电脑连在同一个局域网内. 比如连接同一个wifi . 2. 关闭电脑的杀毒软件和防火墙,这步很重要,一般问题出在这一步 . 3. 打开cmd窗口, 输入ipconfig,找到 IPv4 地 ...

  2. 分享一款不需要注册就可以使用的局域网内手机、电脑、智能电视间的投影APP--“幕享”

    先上APP下载地址: 1.官网(官网加载比较慢): 2.百度云镜像(链接失效可以在评论区留言). 链接:https://pan.baidu.com/s/1o_HZNw_Z4-HSFnEmCJl1Jg ...

  3. 安卓手机和电脑之间传文件最方便的方法

    市面上利用WIFI在手机和电脑传文件的软件,总是要你去下载一个非常变态的电脑客户端.那种客户端在你电脑上装很多恶心的驱动,而且你每次连接手机,都会弹一大堆窗口. android手机是一个小型电脑,具有 ...

  4. Computer:局域网内多台电脑共享文件夹图文教程之详细攻略

    Computer:局域网内多台电脑共享文件夹图文教程之详细攻略 目录 局域网内多台电脑共享文件夹图文教程 1.启用来宾帐户 2.安装NetBEUI协议 3.查看本地安全策略设置是否禁用了GUEST账号 ...

  5. 同一局域网内的其他电脑访问我的电脑本地的网站

    一般情况下,如果本地网站可以被自己的电脑访问,就也可以被同一局域网内的其他电脑访问. 本地电脑的访问方式: localhost 127.0.0.1 192.168.1.122 (本地电脑的局域网 IP ...

  6. 如何访问同一局域网内的其他电脑文件

    如何访问同一局域网内的其他电脑文件 此处为作者写的一个类: using System.Collections.Specialized; /// <summary> /// get hbld ...

  7. 解决电脑不能被局域网内的其它电脑访问的方法

    解决电脑不能被局域网内的其它电脑访问的方法 旁注:本人转载的文章都会有这么一段话:本文转载子互联网,根据我自己地实践和经验做了一些删改. Win7系统的网络功能比XP有了进一步的增强,使用起来也相对清 ...

  8. Windows下其他电脑访问局域网内某一电脑上的图片

    一.场景 假设在局域网下,A电脑想要访问B电脑上的图片或其他文件(不使用文件共享),如何处理?这时有一种解决办法,就是IIS发布一个网站,然后在站点设置一个存放图片的文件夹的路径,这样其他电脑访问时, ...

  9. 手机局域网关闭计算机的方法,局域网怎么防泄密?禁止电脑手机互传文件方法...

    现在的智能手机,就像个人电脑一样,具有独立的操作系统,独立的运行空间,可以通过应用商店下载和安装任意软件,包括办公软件.视频软件.聊天软件.游戏软件.炒股软件等等.此外,我们还可以把手机当做U盘或者移 ...

最新文章

  1. Java 内存泄漏监控检测与分析
  2. Android 页面进行镜像反转-面试
  3. numpy 深复制 切片创建视图
  4. 十年,我的计算机生涯
  5. 分布式计算平台:Dryad
  6. Linux线程(二)
  7. 快速实现一个分布式定时器
  8. 我们在tool里给ui element设置断点,然后操作的时候,断点就触发了。Framework是咋实现的
  9. MySQL之命令mysql -- MySQL服务器的客户端工具
  10. css中em与px的介绍及换算方法
  11. armv7 cortex a系列编程手册_AWTK能为现代GUI编程带来何种改变?
  12. MySQL实现分页查询
  13. java itext word_Java使用iText生成word文件的解决方案 | 学步园
  14. 【Teradata】tdlocaledef修改默认日期配置
  15. mysql存储过程详细教程
  16. Android应用程序版本号管理(官方文档中文版) 2011-08-07 22:03:36
  17. python——设置渐变色
  18. 内温的整体优先效应实验_认知
  19. python类似图片查找_python检测相似图片
  20. 工程师的爱情故事之1:大发明家

热门文章

  1. 打击玩家不良行为!《彩虹六号:围攻》新的信誉系统
  2. 在线MD5加密解密工具
  3. c++笔试题大整合(完整)
  4. 通过wifi网络共享网络的方式连接PYNQ开发板
  5. 兴业数金 测试 面试真题|面经
  6. Delphi 控件开发步骤
  7. DMZ-demilitarized zone 隔离区
  8. 火影忍者网页设计html,html+css+js实现火影背景切换登录页面
  9. 如何经营好一间咖啡店,有哪些工作要做?
  10. mysql的text的默认值,mysql给text设置一个默认值(干货)