一直不喜欢框架,不喜欢引入外部包,所以重新梳理了以下代码,尽量不引入外部依赖。主要实现以下目标(玩具级代码,适合精准需求的小应用):
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用户同时写数据库应加锁,大家自己补充代码吧。