zoukankan      html  css  js  c++  java
  • 纯go+sqlite3+html/template网站(类似MVC实现数据库CURD、上传下载文件)-最少依赖才是硬道理

    一直不喜欢框架,不喜欢引入外部包,所以重新梳理了以下代码,尽量不引入外部依赖。主要实现以下目标(玩具级代码,适合精准需求的小应用):

    1、可以操作sqlite3数据库

    2、可以用template展示数据(需增加页面时,在v目录下增加模板html文件,在main.go中增加路由及响应函数)

    3、可以上传下载文件(参考https://zhuanlan.zhihu.com/p/91606240

    思路:

    1、路由:参考:https://blog.csdn.net/weixin_33691817/article/details/91920458

    2、Model层:将上文中sqlite3 CRUD操作封装为一个模块db/db.go,根据CRUD需要,修改此模块,在main.go中调用(还是觉得过程式思维省脑筋,就写成过程式函数,没有面向对象,Control层也直接写入main.go中)。

    3、建一个目录v,存放所有html模板(View层),用template.ParseFiles载入及展示。(脑筋不够,感觉用MVC比MVVM容易些)

     目录结构

    -htmlsql3(根目录)

    -db(存放sqlite3数据库和db.go的目录)

    --db.go

    -v(存放html模板的目录,其中有index.html和upload.html)

    --static(存放静态文件及上传文件的目录)

    -main.go

    4 、建一个名为htmlsql3的目录,其中就2两个go文件和2个html文件。

    main.go

    //在main.go同级目录建两个子目录:db和v,
    //db目录下db.go,封装操作db.db的sql语句
    //v目录下是html模板,用于呈现网页内容;router和controller直接写在main.go中
    package main
    
    import (
        "fmt"
        "html/template"
        "htmlsql3/db"
        "io/ioutil"
        "net/http"
        "regexp"
    )
    
    func main() {
        //静态文件服务
        http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
        http.HandleFunc("/", Route)
        http.ListenAndServe(":8000", nil)
    
    }
    
    // 路由定义
    type routeInfo struct {
        pattern string                                       // 正则表达式
        f       func(w http.ResponseWriter, r *http.Request) //Controller函数
    }
    
    // 路由添加
    var routePath = []routeInfo{
        routeInfo{"^/a$", IndexHandler},       //首页
        routeInfo{"^/upload$", UploadHandler}, //上传页
    }
    
    // 使用正则路由转发
    func Route(w http.ResponseWriter, r *http.Request) {
        isFound := false
        for _, p := range routePath {
            // 这里循环匹配Path,先添加的先匹配
            reg, err := regexp.Compile(p.pattern)
            if err != nil {
                continue
            }
            if reg.MatchString(r.URL.Path) {
                isFound = true
                p.f(w, r)
            }
        }
        if !isFound {
            // 未匹配到路由
            fmt.Fprint(w, "404 Page Not Found!")
        }
    }
    
    func IndexHandler(w http.ResponseWriter, r *http.Request) {
        // 解析指定文件生成模板对象
        tem, err := template.ParseFiles("v/index.html")
        checkErr(err)
        // 从数据库获取数据用于渲染模板
        maxid := db.GetMaxID()
        Data := DbData{
            Maxid: maxid}
        //渲染输出
        tem.Execute(w, Data)
    }
    
    func UploadHandler(w http.ResponseWriter, r *http.Request) {
        if r.Method == "POST" { //POST方式进行文件上传至static目录下
            //获取普通表单数据
            username := r.FormValue("username")
            fmt.Println(username)
            //获取文件流,第三个返回值是错误对象
            file, header, _ := r.FormFile("upfile")
            //上传文件大小类型限制可参考https://studygolang.com/articles/22643
            //读取文件流为[]byte
            b, _ := ioutil.ReadAll(file)
            //把文件保存到指定位置
            ioutil.WriteFile("./static/"+header.Filename, b, 0777)
            //输出上传时文件名
            fmt.Println("上传文件名:", header.Filename)
        } else if r.Method == "GET" { //GET方式显示上传页面
            // 解析指定文件生成模板对象
            tem, err := template.ParseFiles("v/upload.html")
            checkErr(err)
            // 从数据库获取数据用于渲染模板
            maxid := db.GetMaxID()
            Data := DbData{
                Maxid: maxid}
            //渲染输出
            tem.Execute(w, Data)
        }
    }
    
    func checkErr(err error) {
        if err != nil {
            fmt.Println("err", err)
            panic(err)
        }
    }
    
    //数据库结构体定义/////////////////
    type DbData struct {
        Maxid int
    }

    db.go

    package db
    
    import (
        "database/sql"
        "fmt"
    
        _ "github.com/mattn/go-sqlite3"
    )
    
    func Create() {
        fmt.Println("打开数据库")
        db, err := sql.Open("sqlite3", "./db/db.db")
        defer db.Close()
        checkErr(err)
    
        //fmt.Println("创建数据表")
        sql_table := `
    CREATE TABLE IF NOT EXISTS "userinfo" (
       "uid" INTEGER PRIMARY KEY AUTOINCREMENT,
       "username" VARCHAR(64) NULL,
       "departname" VARCHAR(64) NULL,
       "created" TIMESTAMP default (datetime('now', 'localtime'))  
    );
    CREATE TABLE IF NOT EXISTS "userdeatail" (
       "uid" INT(10) NULL,
       "intro" TEXT NULL,
       "profile" TEXT NULL,
       PRIMARY KEY (uid)
    );
       `
        db.Exec(sql_table)
    }
    
    func Insert() {
        fmt.Println("打开数据库")
        db, err := sql.Open("sqlite3", "./db/db.db")
        defer db.Close()
        checkErr(err)
    
        //插入数据
        fmt.Print("插入数据, ID=")
        stmt, err := db.Prepare("INSERT INTO userinfo(username, departname)  values(?, ?)")
        checkErr(err)
        res, err := stmt.Exec("astaxie", "研发部门")
        checkErr(err)
        id, err := res.LastInsertId()
        checkErr(err)
        fmt.Println(id)
    }
    
    func GetMaxID() int {
        fmt.Println("打开数据库")
        db, err := sql.Open("sqlite3", "./db/db.db")
        defer db.Close()
        checkErr(err)
    
        //查询数据
        fmt.Println("查询数据")
        rows, err := db.Query(" SELECT MAX(uid) AS `MAXUID`  FROM  userinfo")
        checkErr(err)
        var uid int
        for rows.Next() {
            err = rows.Scan(&uid)
            checkErr(err)
            fmt.Println(uid)
        }
        return int(uid)
    }
    
    func Update() {
        fmt.Println("打开数据库")
        //先用MAX查询最大uid
        id := GetMaxID()
        db, err := sql.Open("sqlite3", "./db/db.db")
        defer db.Close()
        checkErr(err)
        //更新数据
        fmt.Print("更新数据 ")
    
        stmt, err := db.Prepare("update userinfo set username=? where uid=?")
        checkErr(err)
        res, err := stmt.Exec("astaxieupdate", id)
        checkErr(err)
        affect, err := res.RowsAffected()
        checkErr(err)
        fmt.Println(affect)
    }
    
    func Retrieve() {
        fmt.Println("打开数据库")
        db, err := sql.Open("sqlite3", "./db/db.db")
        defer db.Close()
        checkErr(err)
    
        //查询数据
        fmt.Println("查询数据")
        rows, err := db.Query("SELECT * FROM userinfo")
        checkErr(err)
        for rows.Next() {
            var uid int
            var username string
            var department string
            var created string
            err = rows.Scan(&uid, &username, &department, &created)
            checkErr(err)
            fmt.Println(uid, username, department, created)
        }
    }
    
    func Delete() {
        //先用MAX查询最大uid
        id := GetMaxID()
        fmt.Println("打开数据库")
        db, err := sql.Open("sqlite3", "./db/db.db")
        defer db.Close()
        checkErr(err)
    
        //删除数据
        fmt.Println("删除数据")
        stmt, err := db.Prepare("delete from userinfo where uid=?")
        checkErr(err)
        res, err := stmt.Exec(id)
        checkErr(err)
        affect, err := res.RowsAffected()
        checkErr(err)
        fmt.Println(affect)
    }
    
    func checkErr(err error) {
        if err != nil {
            panic(err)
        }
    }

    index.html

    hello {{.Maxid}}

    upload.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>文件上传</title>
    </head>
    <body>
    <form action="/upload" enctype="multipart/form-data" method="post">  
        用户名:<input type="text" name="username"/><br/> 
        上传:<input type="file" name="upfile"/><br/>
        <input type="submit" value="提交"/>
    </form>
    </body>
    </html>

    忽然想到重要的事,多个web用户同时写数据库应加锁,大家自己补充代码吧。

  • 相关阅读:
    Mysql多实例
    nginx不间断服务平滑升级
    源码编译安装mysql
    nginx优化后的主配置文件
    Nginx安装与应用
    Android 7 修改启动动画和开机声音
    Android 7 默认声音/大小修改
    高通与At指令:ATFWD解析
    高通与At指令:AtCop解析
    高通与At指令:基础概念
  • 原文地址:https://www.cnblogs.com/pu369/p/12290862.html
Copyright © 2011-2022 走看看