Go实操(二):时间处理

1.获取当前时间戳

1
fmt.Println(time.Now().Unix()) //1561713322

2.格式化时间

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
fmt.Println("时间:",time.Now().Format("20060102150405"))
fmt.Println("时间1:",time.Now().Format("2006/01/02 15:04:05"))
fmt.Println("时间2:",time.Now().Format("2006-01-02 15:04:05"))

/*
输出:

时间: 20190628172052
时间1: 2019/06/28 17:20:52
时间2: 2019-06-28 17:20:52
时间3: 2019-06-28
时间4: 17:20:52

*/

为什么是"2006-01-02 15:04:05",这是个奇葩,必须是这个时间点, 据说是go诞生之日, 记忆方法:6-1-2-3-4-5

Go学习(二十七):反射使用

1.介绍

Go语言实现了反射,所谓反射就是动态运行时的状态。我们一般用到的包是reflect包,reflect 实现了运行时的反射能力,能够让程序操作不同类型的对象。反射包中有两对非常重要的函数和类型,两个函数分别是:

Go学习(二十六):Md5-Sha1-Base64

1.生成Md5

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main
import (
	"crypto/md5"
	"fmt"
	"io"
)
func main() {
	str := "Hello Word"
	// 方法一
	sum := md5.Sum([]byte(str))
	// 转成16进制
	md51 := fmt.Sprintf("%x", sum)
	fmt.Println("方法一: " + md51)

	// 方法二
	hash := md5.New()
	// 将str写入到hash
	_, err := io.WriteString(hash, str)
	if err != nil {
		fmt.Println(err.Error())
	}
	// 转成16进制
	md52 := fmt.Sprintf("%x",hash.Sum(nil))
	fmt.Println("方法二: " + md52)
}

/** 输出
方法一: ed0a96e83ab7b0910fcbcc131b2e6b82
方法二: ed0a96e83ab7b0910fcbcc131b2e6b82
*/

2.生成Sha1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main
import (
	"crypto/sha1"
	"fmt"
	"io"
)
func main() {
	str := "1234"
	// 方法一
	sum := sha1.Sum([]byte(str))
	// 转成16进制
	shaA := fmt.Sprintf("%x", sum)
	fmt.Println("方法一: " + shaA)

	// 方法二
	hash := sha1.New()
	_, err := io.WriteString(hash, str)
	if err != nil {
		fmt.Println(err.Error())
	}
	// 转成16进制
	shaB := fmt.Sprintf("%x",hash.Sum(nil))
	fmt.Println("方法二: " + shaB)
}
/** 输出
方法一: 7110eda4d09e062aa5e4a390b0a572ac0d2c0220
方法二: 7110eda4d09e062aa5e4a390b0a572ac0d2c0220
*/

3.Base64编码和解码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main
import (
	"encoding/base64"
	"fmt"
)
func main() {
	str := "1234"
	// 方法一
	// Base64编码
	base64EncodeStr := base64.StdEncoding.EncodeToString([]byte(str))
	fmt.Printf("方法一,Base64编码: %v \n",base64EncodeStr)
	// Base64解码
	decodeString, err := base64.StdEncoding.DecodeString(base64EncodeStr)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("方法一,Base64解码: %s \n",decodeString)

	// 方法二:使用兼容URL的base64编码和解码
	toString := base64.URLEncoding.EncodeToString([]byte(str))
	fmt.Printf("方法二,Base64编码: %v \n",toString)
	bytes, _ := base64.URLEncoding.DecodeString(toString)
	fmt.Printf("方法二,Base64解码: %s \n",bytes)
}

/**输出
方法一,Base64编码: MTIzNA== 
方法一,Base64解码: 1234 
方法二,Base64编码: MTIzNA== 
方法二,Base64解码: 1234 
*/

Go学习(二十五):操作数据库-MYSQL

1.介绍

Go官方提供了database包,database包下有sql/driver。该包用来定义操作数据库的接口,这保证了无论使用哪种数据库,操作方式都是相同的。但Go官方并没有提供连接数据库的driver,如果要操作数据库,还需要第三方的driver包。

Go学习(二十四):文件操作

1.介绍

在Go语言中,文件操作的大多数函数都在os包里面,文件的信息包括文件名、文件大小、修改权限、修改时间等。

1.1 文件接口属性(FileInfo)

1
2
3
4
5
6
7
8
type FileInfo interface {
	Name() string       // 文件名
	Size() int64        // 文件大小
	Mode() FileMode     // 修改权限
	ModTime() time.Time // 最后修改时间
	IsDir() bool        // 判断是否是目录
	Sys() interface{}   // 文件的详细信息,获取的值是*syscall.Stat_t类型 
}

1.2 文件信息结构体(fileStat)

1
2
3
4
5
6
7
type fileStat struct {
	name    string
	size    int64
	mode    FileMode
	modTime time.Time
	sys     syscall.Stat_t
}
1.fileStat结构体的常用方法汇总
方法名 描述
Name() string 返回文件名
IsDir() bool 判断是否是目录
Size() int64 返回文件大小
Mode() FileMode 返回文件权限
ModTime() time.Time 文件的最后修改时间
Sys() interface{} 文件的详细信息,获取的值是*syscall.Stat_t类型
2.使用示例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main
import (
	"fmt"
	"os"
)
func main(){
	fp := "/Users/shershon/ProjectItem/GoItem/go-basic/app/file/public/test1.txt"
	fileInfo, err := os.Stat(fp)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("类型: %T\n",fileInfo)
	fmt.Printf("文件名: %v\n",fileInfo.Name())
	fmt.Printf("文件大小: %v\n",fileInfo.Size())
	fmt.Printf("是否为目录: %v\n",fileInfo.IsDir())
	fmt.Printf("文件权限: %v\n",fileInfo.Mode())
	fmt.Printf("文件最后修改的时间: %v\n",fileInfo.ModTime())
}
/**输出
类型: *os.fileStat
文件名: test1.txt
文件大小: 11
是否为目录: false
文件权限: -rw-r--r--
文件最后修改的时间: 2021-01-15 16:29:53.816034553 +0800 CST
sys: &{Dev:16777220 Mode:33188 Nlink:1 Ino:15440327 Uid:501 Gid:20 Rdev:0 Pad_cgo_0:[0 0 0 0] Atimespec:{Sec:1610699394 Nsec:591237565} Mtimespec:{Sec:1610699393 Nsec:816034553} Ctimespec:{Sec:1610699393 Nsec:816034553} Birthtimespec:{Sec:1610611801 Nsec:544367649} Size:11 Blocks:8 Blksize:4096 Flags:0 Gen:0 Lspare:0 Qspare:[0 0]}
*/

2.求路径方法

2.1 方法列表

方法的所属包: path/filepath

Go学习(二十三):JSON使用

1.Map转JSON

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main
import (
	"encoding/json"
	"fmt"
)
func main()  {
	// map里面
	map1 := map[string]string {
		"name":"张三",
		"age":"18",
		"home":"北京",
	}
	// 将map解析成json
	json1, err := json.Marshal(map1)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("json1: %s  类型: %T \n",json1,json1)
  
	map2 := map[string][]string {
		"fruit": {"香蕉","葡萄"},
		"coder":{"PHP","Go","Java"},
		"likes":{"打游戏","看动漫"},
	}
	json2, err := json.Marshal(map2)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("json2: %s  类型: %T \n",json2,json2)
}
/**输出
json1: {"age":"18","home":"北京","name":"张三"}  类型: []uint8 
json2: {"coder":["PHP","Go","Java"],"fruit":["香蕉","葡萄"],"likes":["打游戏","看动漫"]}  类型: []uint8 
*/

2. Json转Map

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main
import (
	"encoding/json"
	"fmt"
)
func main()  {
	json1 := `{"age":"18","home":"北京","name":"张三"}`
	map1 := make(map[string]string)
	err := json.Unmarshal([]byte(json1), &map1)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("map1: %v T: %T\n",map1,map1)

	json2 := `{"coder":["PHP","Go","Java"],"fruit":["香蕉","葡萄"],"likes":["打游戏","看动漫"]}`
	map2 := make(map[string][]string)
	err = json.Unmarshal([]byte(json2), &map2)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("map2: %v T: %T\n",map2,map2)
}
/**输出
map1: map[age:18 home:北京 name:张三] T: map[string]string
map2: map[coder:[PHP Go Java] fruit:[香蕉 葡萄] likes:[打游戏 看动漫]] T: map[string][]string
*/

3.结构体转JSON

3.1 无字段标签

结构体转换成JSON在开发中经常会用到。encoding/json包是通过反射机制来实现编解码的,因此结构体必须导出所转换的字段,没有导出的字段不会被encoding/json包解析。

Go学习(二十二):Web开发-HTTP客户端的实现

1.怎么实现HTTP协议客户端?

Go语言标准库内置了net/http包,涵盖了HTTP客户端和服务端具体的实现方式。内置的net/http包提供了最简洁的HTTP客户端实现方式,无须借助第三方网络通信库,就可以直接使用HTTP中用得最多的GET和POST方式请求数据。

Go学习(二十一):Web开发-HTTP服务端接收参数的几种方式

1.获取GET参数

1.1 方法列表

方法名 描述
(r *Request) ParseForm() error 判断是否解析传参时出错
(r *Request) FormValue(key string) string 接收指定key的参数值

1.2 使用示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 服务端代码
package main
import (
	"fmt"
	"net/http"
)
func main() {
	http.HandleFunc("/login", login)
	_ = http.ListenAndServe(":8888", nil)
}
func login(w http.ResponseWriter, r *http.Request) {
  // 判断参数是否是Get请求,并且参数解析正常
	if r.Method == "GET" && r.ParseForm() == nil {
    // 接收参数
		userName := r.FormValue("userName")
		fmt.Printf("userName: %s \n", userName)
		passWord := r.FormValue("passWord")
		fmt.Printf("passWord: %s \n", passWord)
		if userName == "" || passWord == "" {
			w.Write([]byte("用户名或密码不能为空"))
		}
		if userName == "admin" && passWord == "1234" {
			w.Write([]byte("登录成功!"))
		} else {
			w.Write([]byte("用户名或密码错误!"))
		}
	}
}

2.获取POST参数

2.1 接收POST参数分以下两种情况

  • 普通的Post表单: Content-Type=application/x-www-form-urlencoded
  • 有文件上传的Post表单: Content-Type=multipart/form-data

2.2 普通的Post表单(r.PostFormValue)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 服务端代码
package main
import (
	"fmt"
	"net/http"
)
func main() {
	http.HandleFunc("/login", login)
	_ = http.ListenAndServe(":8888", nil)
}
// 登录
func login(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" && r.ParseForm() == nil {
		// 接收post参数
		userName := r.PostFormValue("userName")
		fmt.Printf("userName: %s \n", userName)
		passWord := r.PostFormValue("passWord")
		fmt.Printf("passWord: %s \n", passWord)
		if userName == "" || passWord == "" {
			w.Write([]byte("用户名或密码不能为空"))
			return
		}
		if userName == "admin" && passWord == "1234" {
			w.Write([]byte("登录成功!"))
			return
		}
		w.Write([]byte("用户名或密码错误!"))
	} else {
		w.Write([]byte("当前接口,仅支持POST请求!"))
	}
	return
}

2.3 有文件上传的Post表单(r.FormFile)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package main
import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
)
func main() {
	http.HandleFunc("/upload", upload)
	_ = http.ListenAndServe(":8888", nil)
}
// 上传文件
func upload(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" && r.ParseForm() == nil {
		// 接收上传文件的参数
		formFile, fileHeader, err := r.FormFile("file")
		if formFile == nil {
			http.Error(w,"上传的文件不能为空!",http.StatusInternalServerError)
			return
		}
		if err != nil {
			http.Error(w,err.Error(),http.StatusInternalServerError)
			return
		}
		// 延迟关闭文件
		defer formFile.Close()
		// 获取上传文件的名称
		filename := fileHeader.Filename
		fmt.Printf("文件名称: %s \n",filename)
		// 获取文件的后缀
		ext := path.Ext(filename)
		fmt.Printf("文件后缀: %s \n",ext)
		// 创建新文件,如果同名文件存在,则会清空
		pathName := "public/img"
		if !pathExist(pathName) {
			err := os.MkdirAll(pathName, os.ModePerm)
			if err != nil {
				http.Error(w,"创建目录失败: "+ err.Error(),http.StatusInternalServerError)
				return
			}
		}
		newFile, err := os.Create(pathName +"/"+ filename)
		if err != nil {
			http.Error(w,"创建文件失败: "+ err.Error(),http.StatusInternalServerError)
			return
		}
		defer newFile.Close()
		// 将formFile复制到newFile,从而实现上传的功能
		written, err := io.Copy(newFile, formFile)
		fmt.Printf("上传结果: %d \n",written)
		if err != nil {
			http.Error(w,"文件上传失败: "+ err.Error(),http.StatusInternalServerError)
			return
		}
		w.Write([]byte("文件上传成功!"))
	}
	return
}
// 判断目录是否存在
func pathExist(path string) bool  {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}
运行结果

image-20210106172843665