golang web实战之三(基于iris框架的 web小应用,数据库采用 sqlite3 )
一、效果:一个图片应用
1、可上传图片到uploads目录。
2、可浏览和评论图片(用富文本编辑器输入)
二、梳理一下相关知识:
1、iris框架(模板输出,session)
2、富文本编辑器、sqlite3(保存评论文字内容)
二、参考MVC设计模式,手工建以下目录结构和文件,- 表示目录层级 \表示目录,
-irisMVC
--main.go //go主程序
--config.json //配置文件
--m\ //model层,处理数据库
---m.go
--v\ //view ,视图
---index.html //首页模板;
---upload.html //上传图片
---comment.html //评论图片
--db\ //保存sqlite3格式数据库
---pic.db //这个文件是在代码中自动生成的
--uploads\ //保存上传图片
--static\ //静态文件,css,js,logo
注:说是MVC,为简化代码,我将controller控制器层、路由都写在main.go中了
另外,可使用项目热重启,参考https://www.cnblogs.com/pu369/p/10691747.html
三、从main.go开始写代码
1、main.go
/* //config.json { "appname": "IrisDemo", "port": 8000 } */ package main import ( "encoding/json" "fmt" "html/template" "io" "io/ioutil" "os" "strconv" "time" "github.com/kataras/iris/sessions" "github.com/kataras/iris" _ "github.com/mattn/go-sqlite3" "irisMVC/m" ) //配置文件结构 type Coniguration struct { Appname string `json:appname` Port string `json:port` CreatedAt time.Time `json:"created"` } //全局变量conf,包含了配置文件内容 var conf Coniguration //初始化 func init() { //读配置文件 file, _ := os.Open("config.json") defer file.Close() decoder := json.NewDecoder(file) err := decoder.Decode(&conf) if err != nil { fmt.Println("Error:", err) } fmt.Println(conf.Port) } func main() { app := iris.New() //session sess := sessions.New(sessions.Config{Cookie: "sessionscookieid", Expires: 45 * time.Minute}) //静态文件目录,如http://localhost:8000/staticwwwurl/1.txt app.StaticWeb("/staticwwwurl", "./mystatic") //获得model层数据库对象,用于操作数据库,同时,初始化数据库 db := m.NewDb() // onInterrupt contains a list of the functions that should be called when CTRL+C/CMD+C or a unix kill //command received. iris.RegisterOnInterrupt(func() { db.ORM.Close() }) tmpl := iris.HTML("./v", ".html") //增加模板函数 tmpl.AddFunc("Itoa", func(i int64) string { return strconv.FormatInt(i, 10) }) //解决富文本输出转义问题 tmpl.AddFunc("unescaped", func(x string) interface{} { return template.HTML(x) }) //注册视图目录 app.RegisterView(tmpl) //路由 //主页 app.Get("/", func(ctx iris.Context) { //绑定数据 ctx.ViewData("Title", conf.Appname) pics := db.ListPic() ctx.ViewData("Pic", pics) // 渲染视图文件: ./v/index.html ctx.View("index.html") }) //评论图片 app.Post("/comment", func(ctx iris.Context) { id := ctx.PostValue("Pid") comment := ctx.PostValue("Comment") db.Comment(id, comment) ctx.HTML("评论成功") }).Name = "comment" //Name用于生成urlpath app.Get("/comment/{Id:int}", func(ctx iris.Context) { id, _ := ctx.Params().GetInt("Id") ctx.ViewData("Pid", id) ctx.View("comment.html") }).Name = "comment1" //上传图片 app.Get("/upload", func(ctx iris.Context) { ctx.View("upload.html") }) //限制上传文件大小10M,硬编码 app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) { // Get the file from the request. file, info, err := ctx.FormFile("uploadfile") if err != nil { ctx.StatusCode(iris.StatusInternalServerError) ctx.HTML("Error while uploading: <b>" + err.Error() + "</b>") return } defer file.Close() fname := info.Filename //创建一个具有相同名称的文件 //假设你有一个名为'upload'的文件夹 //出于安全需要,可以在这里过滤文件类型,处理文件名后缀 out, err := os.OpenFile("./upload/"+fname, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { ctx.StatusCode(iris.StatusInternalServerError) ctx.HTML("Error while uploading: <b>" + err.Error() + "</b>") return } defer out.Close() io.Copy(out, file) //可以将,看成=,可以理解为:将file的值赋给out //然后将fname存入数据库pic.db的PIC表 db.SavePic(fname) ctx.HTML("上传成功") }) //下载图片 app.Get("/pic/{url}", func(ctx iris.Context) { //我将一张图片改名为3.html,并复制到upload文件夹中。 //下面两行是直接下载图片 //file := "./upload/3.html" //ctx.SendFile(file, "c.jpg") //下面是在网页上显示图片 ctx.Header("Content-Type", "image/png") ctx.Header("Content-Disposition", fmt.Sprint("inline; filename=\"aaa\"")) file, err := ioutil.ReadFile("./upload/3.html") if err != nil { ctx.HTML("查无此图片") return } ctx.Write(file) }) //session的简单使用 app.Get("/admin", func(ctx iris.Context) { //可在这里用sess.UseDatabase,可参考:https://blog.csdn.net/wangshubo1989/article/details/78344183 s := sess.Start(ctx) //set session values s.Set("login name", "iris session") i, err := s.GetInt("num") if err != nil { i = 0 } i += 1 s.Set("num", i) ctx.HTML(fmt.Sprintf("%s %d", s.GetString("name"), i)) }) app.Run(iris.Addr(":" + conf.Port)) }
2.config.json
{ "appname": "IrisDemo 1.0", "port": "8000" }
3.m.go
package m import ( "strconv" "time" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) //数据库对象DB,对xorm进行了包装 //注意骆峰法 type Db struct { ORM *xorm.Engine } //数据库表 //Admin 管理员表 type Admin struct { ID int64 // xorm默认自动递增 A管理员名 string Password string `xorm:"varchar(200)"` CreatedAt time.Time `xorm:"created"` } //Pic 用户对图片评论的表 type Pic struct { Id int64 // xorm默认自动递增 Url string //图片保存路径 Comment string `xorm:"text"` //评论 CreatedAt time.Time `xorm:"created"` } //User 用户表 type User struct { Id int64 // xorm默认自动递增 A用户名 string Password string `xorm:"varchar(200)"` CreatedAt time.Time `xorm:"created"` } //生成数据库 func NewDb() *Db { //初始化数据库 orm, _ := xorm.NewEngine("sqlite3", "./db/db.db") //此时生成数据库文件db/picdb.db,以及表USER、PIC _ = orm.Sync2(new(User), new(Pic)) db := new(Db) db.ORM = orm return db } func (d *Db) SavePic(url string) { pic := new(Pic) pic.Url = url d.ORM.Insert(pic) } func (d *Db) ListPic() []Pic { var p []Pic _ = d.ORM.Sql("select * from pic LIMIT 3 OFFSET 0").Find(&p) //以下三句没解决富文本被当成string输出的问题。最后采取的办法是:注册模板函数unescaped //for i := range p { // p[i].Comment = template.HTMLEscaper(p[i].Comment) // fmt.Println(p[i].Comment) //} return p } func (d *Db) Comment(id, comment string) { var p Pic idi, _ := strconv.ParseInt(id, 10, 64) _, _ = d.ORM.ID(idi).Get(&p) p.Comment += comment //id为int64或string都可以 d.ORM.ID(id).Update(p) }
4.几个html模板
4.1 index.html
<!DOCTYPE html> <head> <meta charset="utf-8"> <title>{{.Title}}</title> </head> <body> {{range $i, $v := .Pic}} {{$v.Id}} {{$v.Url}} {{$v.CreatedAt}} {{$v.Comment | unescaped}} <a href={{urlpath "comment" }}/{{Itoa $v.Id}}>点评</a> <a href={{urlpath "comment1" $v.Url}}>未实现</a> <img src=/pic/{{$v.Url}}><br/> {{end}} </body> </html>
4.2 upload.html
<!DOCTYPE html> <head> <meta charset="utf-8"> <title>{{.Title}}</title> </head> <body> <form enctype="multipart/form-data" action="upload" method="POST"> <input type="file" name="uploadfile" /> <input type="hidden" name="token" value="{{.}}" /> <input type="submit" value="upload" /> </form> </body> </html>
4.3 comment.html
<!DOCTYPE html> <head> <meta charset="utf-8"> <title>{{.Title}}</title> </head> <body> <form action="/comment" method="post"> Id: <input type="text" name="Pid" value={{.Pid}} /> 评论: <input type="text" id="Comment" name="Comment" /> <br /> <input type="submit" value="提交"/> </form> <!-- 注意, 只需要引用 JS,无需引用任何 CSS !!!--> <script src="https://cdn.bootcss.com/wangEditor/10.0.13/wangEditor.min.js"></script> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <div id="div1"> <p>这句必须在段落标签内</p> </div> <script type="text/javascript"> var E = window.wangEditor var editor = new E('#div1') var $text1 = $('#Comment') editor.customConfig.onchange = function (html) { // 监控变化,同步更新到 input $text1.val(html) } editor.create() // 初始化 input 的值 $text1.val(editor.txt.html()) </script> </body> </html>
参考:
理解Golang包导入https://studygolang.com/articles/3189
图片输出 https://www.jianshu.com/p/583f473fe2c2、 https://blog.csdn.net/ssssny/article/details/77717287
session https://blog.csdn.net/wangshubo1989/article/details/78344183
富文本的显示问题 https://yq.aliyun.com/articles/348899 https://studygolang.com/articles/1741
还可通过 https://sourcegraph.com搜索相应的源码、https://gocn.vip/question/822
修改数组元素的问题 https://blog.csdn.net/xiaoquantouer/article/details/80392656
数据类型转换 https://blog.csdn.net/u013485530/article/details/80906569