zoukankan      html  css  js  c++  java
  • 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}}&nbsp;&nbsp;{{$v.Url}}&nbsp;&nbsp;{{$v.CreatedAt}}&nbsp;&nbsp;{{$v.Comment | unescaped}}&nbsp;&nbsp;<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

  • 相关阅读:
    【Eclipse】怎样把代码复制到word中并保持颜色
    windows下配置gvim
    这是给开发者的弥天大谎还是至理名言?
    Linux中常用软件安装(基于Ubuntu)
    MyEclipse 9.1优化技巧
    【数据库复习】函数依赖
    Windows下使用Flex入门
    【数据库复习】SQL
    浏览器中的“Linux”
    Unity开发原则
  • 原文地址:https://www.cnblogs.com/pu369/p/10798094.html
Copyright © 2011-2022 走看看