《Go语言编程》第五章完整示例修改版

作者:Ajian 发布时间:October 26, 2012 分类:Golang

      最近一直在看《Go语言编程》这本书,书的结构和内容都是很不错的,可惜小毛病不少,而且可能不太适合编程初学的人学习,我的编程功底不高,加上之前对于C C++类的语言写得较少,所以也看得挺费劲的。第五章的完整示例是一个不错的学习例子,层层递进,讲得很不错。最后的示例代码可能多种原因,并不能直接的使用,现在进行了修改可以使用,动静分离的还没有测试,这个例子还在丰富,做一个小相册出来玩玩。

下面是具体的示例,

具体的FAQ看 http://sf.com  http://segmentfault.com/q/1010000000129720  有本书的作者进行解答。

package main

import (
    "io"
    "os"
    "log"
    "net/http"
    "io/ioutil"
    "html/template"
    "path"
    "strings"
    "runtime/debug"
)

const(
    UPLOAD_DIR = "./uploads"
    TEMPLATE_DIR = "./views"
    ListDir= 0x0001
)

var templates = map[string]*template.Template{}

func init(){
    fileInfoArr,err := ioutil.ReadDir( TEMPLATE_DIR )
    if err != nil {
        panic( err )
        return
    }
    var templateName,templatePath string 
    for _,fileInfo := range fileInfoArr{
        templateName = fileInfo.Name()
        if ext := path.Ext( templateName);ext != ".html" {
            continue
        }
        templatePath= TEMPLATE_DIR + "/" +templateName
        log.Println( "Loading template:",templatePath )
        t := template.Must( template.ParseFiles( templatePath))
        tmpl := strings.Split( templateName,".html" )[0]
        templates[tmpl] = t
    }
}

func check( err error ){
    if err!=nil{
        panic( err )
    }
}

func renderHtml( w http.ResponseWriter,tmpl string,locals map[string]interface{}) ( err error) {
        err = templates[tmpl].Execute( w,locals )
        check( err )
        return
}

func isExists( path string ) bool {
    _,err := os.Stat( path )
    if err == nil {
        return true
    }
    return os.IsExist( err )
}

func uploadHandler( w http.ResponseWriter,r *http.Request ){
    if r.Method == "GET" {
        err := renderHtml( w,"upload",nil )
        check( err )
//        io.WriteString( w,"<html><body><form method=\"POST\" action=\"/upload\" enctype=\"multipart/form-data\">"+
//        "Choose an image to upload:<input name=\"image\" type=\"file\" />"+
//        "<input type=\"submit\" value=\"Upload\" />"+
//        "</form></body></html>")
        return
    }

    if r.Method == "POST" {
        f,h,err := r.FormFile( "image" )
        check( err )
        filename := h.Filename
        defer f.Close()
        t,err := os.Create( UPLOAD_DIR + "/" + filename  )
        check( err )
        defer t.Close()
        _,err = io.Copy( t,f )
        check( err ) 
        http.Redirect( w,r,"/view?id="+filename,
        http.StatusFound)
    }
}

func viewHandler( w http.ResponseWriter,r *http.Request ){
    imageId := r.FormValue( "id" )
    imagePath := UPLOAD_DIR + "/" +imageId
    if exists := isExists( imagePath );!exists{
        http.NotFound( w,r )
        return
    }
    w.Header().Set( "Content-Type","image/png" )
    http.ServeFile( w,r,imagePath )
}

func listHandler( w http.ResponseWriter,r *http.Request){
    fileInfoArr,err := ioutil.ReadDir( "./uploads" )
    check( err )
    locals := make( map[string]interface{})
    images:=[]string{}
    for _,fileInfo := range fileInfoArr{
        images = append( images,fileInfo.Name() )
    }

    locals["images"] = images
    err = renderHtml( w,"list",locals )
    check( err )
//    t,err:=template.ParseFiles( "list.html" )
//    var listHtml string
//    for _,fileInfo := range fileInfoArr{
//        imgid := fileInfo.Name()
//        listHtml += "<li><a href=\"/view?id="+imgid+"\">"+imgid+"</a></li>"
//    }
//    io.WriteString( w,"<html><ol>"+listHtml+"</ol></html>" )
}

func safeHandler( fn http.HandlerFunc ) http.HandlerFunc{
    return func( w http.ResponseWriter,r *http.Request ){
        defer func(){
            e := recover()
            if err,ok:= e.(error);ok{
                http.Error( w,err.Error(),http.StatusInternalServerError )
                log.Println( "WARN: panic in %v. - %v",fn,err )
                log.Println( string( debug.Stack() ) )
            }
        }()
        fn( w,r )
    }
}

func staticDirHandler( mux *http.ServeMux,prefix string,staticDir string,flags int ){
    mux.HandleFunc( prefix,func( w http.ResponseWriter,r *http.Request) {
        file := staticDir + r.URL.Path[len( prefix )-1:]
        if( flags & ListDir )== 0{
            if exists := isExists( file );!exists{
                http.NotFound( w,r )
                return
            }
        }
        http.ServeFile( w,r,file )
    })
}

func main(){
    mux := http.NewServeMux()
    staticDirHandler( mux,"/assets/","./public",0 )
    mux.HandleFunc("/",safeHandler(listHandler))
    mux.HandleFunc("/view",safeHandler(  viewHandler ))
    mux.HandleFunc("/upload",safeHandler( uploadHandler ))
    err := http.ListenAndServe( ":8080",mux )
    if err != nil {
        log.Fatal( "ListenAndServe: ",err.Error() )
    }
}

Golang: 注意Channel的特性

作者:Ajian 发布时间:June 12, 2012 分类:Golang

  之前用channel练习的时候遇到一个问题,看似好像代码合理,而且编译也不会有问题,但忘记了一个重要的channel特性。

正确的代码:

package main
import "fmt"

func foo(){
    defer fmt.Println("World")
    fmt.Println("Hello")
}

func sum(x,y int,c chan int){
    c <- x + y
}

func main(){
    foo()
    c := make (chan int);
    go sum(24,18,c)
    fmt.Println(<-c);
}

编译输出//defer的作用应该知道吧

Hello

World

42

如果我把代码改成:
package main
import "fmt"

func foo(){
    defer fmt.Println("World")
    fmt.Println("Hello")
}

//func sum(x,y int,c chan int){
//    c <- x + y
//}

func main(){
    foo()
//    c := make (chan int);
//    go sum(24,18,c)
//    fmt.Println(<-c);
    c := make (chan int)
    d := 2
    c <- d+3 
    fmt.Println(<-c)
}

或者修改为

package main
import "fmt"

func foo(){
    defer fmt.Println("World")
    fmt.Println("Hello")
}

func sum(x,y int,c chan int){
    c <- x + y
}

func main(){
    foo()
    c := make (chan int);
    sum(24,18,c)
    fmt.Println(<-c);
}

修改的第一个是直接没有使用sum函数,赋值给管道之后直接打印出来,第二个修改是只是去掉了go,编译都可以通过,但是执行出来是

Hello
World
throw: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /root/Dropbox/code/go/channel.go:16 +0x5e

goroutine 2 [syscall]:
created by runtime.main
        /tmp/bindist046461602/go/src/pkg/runtime/proc.c:221

这是为什么呢,看似合理的程序,是忽略了Channel是阻塞的,如果没有使用go Channel就一直在阻塞的状态,执行就死循环了。这个特性也在很多场合带来了方便。

Golang: Simple HTTP Server

作者:Ajian 发布时间:June 12, 2012 分类:Golang

   最近还在学习Go语言,当然得多练习,尤其对于我这样的一个半路出家的人来说,对很多的专业术语和编程思想都不够,最近要学习的知识点和事情(随便记录下),实现文件持久化gob,进程锁的概念,channel的灵活使用,http proxy ,如何写自己的配置文件token,如何来检查go程序的性能瓶颈pprof 等,慢慢来也得快快学。

今天实验了下http server , 参考了Andrew Gerrand 的goto程序 https://github.com/nf

 

package main
import(
    "fmt"
    "net/http"
    "io/ioutil"
    "log"
    "time"
)

func Handler( w http.ResponseWriter,r *http.Request ){
    path := r.URL.Path[1:]   
    if path == "favicon.ico"  {
        http.NotFound(w, r)
        return
    }
    if path == ""{
        path = "index.html"
    }
    contents,err:= ioutil.ReadFile( path )
    if err !=nil{
        fmt.Fprintf( w,"404" )
        return
    }
    fmt.Fprintf( w,"%s\n",contents )
}

func Add( w http.ResponseWriter,r *http.Request ){
    name := r.FormValue("name")
    age := r.FormValue("age")
    if name == "" || age == "" {
        fmt.Fprint(w, AddForm)
        return
    }
    fmt.Fprintf(w, "Save : Your name is  %s , You age is %s",name,age)
}

func main(){
    http.HandleFunc( "/",Handler)
    http.HandleFunc( "/add",Add)
    s := &http.Server{  
        Addr:           ":8080",
        ReadTimeout:    30 * time.Second,
        WriteTimeout:   30 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    log.Fatal(s.ListenAndServe())
}

const AddForm = `
<html><body>
<form method="POST" action="/add">
Name: <input type="text" name="name">
Age: <input type="text" name="age">
<input type="submit" value="Add">
</form>
</body></html>
`

Golang: 扫描目录并获取相关信息Scan Directory and Get File Information

作者:Ajian 发布时间:May 18, 2012 分类:Golang

前言:最近看到Go里面有一个func很容易就可以扫描整个目录,并且可以得到相应的目录和文件信息,所以我将其进行了封装,拿到file info的所有信息 这样就可以方便的做其它用途了。

直接上代码,代码基于Go version 1

package main

import (
    "path/filepath"
    "os"
    "flag"
    "fmt"
    "time"
)

const (
    IsDirectory             = iota
    IsRegular
    IsSymlink
)

type sysFile struct {  
    fType       int
    fName       string
    fLink       string
    fSize       int64
    fMtime      time.Time
    fPerm       os.FileMode
}

type F struct {
    files []*sysFile
}

func (self *F) visit(path string, f os.FileInfo, err error) error {
    if ( f == nil ) {
        return err
    }
    var tp int
    if f.IsDir() {
        tp = IsDirectory
    }else if (  f.Mode() & os.ModeSymlink ) > 0 {
        tp = IsSymlink 
    }else{
        tp = IsRegular
    }
    inoFile := &sysFile{
        fName : path,
        fType : tp,
        fPerm : f.Mode(),
        fMtime: f.ModTime(),
        fSize : f.Size(),
    }
    self.files = append( self.files, inoFile )
    return nil
} 


func main() {
    flag.Parse()
    root := flag.Arg(0)
    self := F{
        files: make( []*sysFile, 0 ),
    }
    err := filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
        return self.visit(path, f, err)
    })

    if err != nil {  
      fmt.Printf("filepath.Walk() returned %v\n", err)
    }

    for _, v := range self.files {
        fmt.Println( v.fName,v.fSize )
    }
}

Golang: RPC Authorization 简单ip安全验证

作者:Ajian 发布时间:May 7, 2012 分类:Golang

前言:写网络服务,总要考虑安全机制,对ip和网段进行判断是最简单的一个验证机制。之后想做一个类似注册式的安全验证机制,既可以减少配置文件的麻烦,又可以很好的进行安全管理。

直接上代码 (基于go version 1 编译)

package main
import(
    "net"
    "fmt"
    "time"
    "strings"
)

func main(){
    IP_ARRAY := "192.168.1.234,192.168.1.47,192.168.2.0/28"
    servPort:=":7272"
    l,err := net.Listen( "tcp",servPort )
    if err != nil {
        fmt.Printf( "Listen is error" )
        return 
    }
    allowList :=strings.Split( IP_ARRAY,"," )
    for{  
        conn,err:=l.Accept()
        if err != nil {
            fmt.Printf( "start connect  is error" )
            return 
        }
        ipAddr:=conn.RemoteAddr()
        Addr := strings.Split( ipAddr.String(), ":")
        rAddr := net.ParseIP( Addr[0] )
        var authorized bool = false
        for v := range allowList{
            _,ipNet,err := net.ParseCIDR( allowList[v] )
            if err != nil{
                fmt.Printf( "parse ip net error" )
                ipHost := net.ParseIP( allowList[v])
                if ipHost != nil{
                   if ipHost.Equal( rAddr ) {
                      authorized =true
                   }
                }else{
                    fmt.Printf( "ip list error" )
                }
            }else{ 
                fmt.Printf( "Contains ip " )
                if ipNet.Contains( rAddr ) {
                    authorized =true
                }
            }
        }
        if authorized == true{  
            curTime:=time.Now()
            fmt.Printf( curTime.Format( "2006-01-02 15:04:05" )  )
            conn.Write( []byte(curTime.Format( "2006-01-02 15:04:05" ) ) )
            time.Sleep( 10)
        }else{
            conn.Close()
        }
    }
}

从不同的网络telnet 看下效果吧。