千字文

分类:散文阅读 - 科普阅读 | 阅读(121) | 发布于:2020-07-15 09:50 | 标签:无

千字文

南北朝 · 周兴嗣

天地玄黄,宇宙洪荒。
日月盈昃,辰宿列张。
寒来暑往,秋收冬藏。
闰余成岁,律吕调阳。
云腾致雨,露结为霜。
金生丽水,玉出昆冈。
剑号巨阙,珠称夜光。
果珍李柰,菜重芥姜。
海咸河淡,鳞潜羽翔。
龙师火帝,鸟官人皇。
始制文字,乃服衣裳。
推位让国,有虞陶唐。
吊民伐罪,周发殷汤。
坐朝问道,垂拱平章。
爱育黎首,臣伏戎羌。
遐迩一体,率宾归王。
鸣凤在竹,白驹食场。
化被草木,赖及万方。
盖此身发,四大五常。
恭惟鞠养,岂敢毁伤。
女慕贞洁,男效才良。
知过必改,得能莫忘。
罔谈彼短,靡恃己长。
信使可覆,器欲难量。
墨悲丝染,诗赞羔羊。
景行维贤,克念作圣。
德建名立,形端表正。
空谷传声,虚堂习听。
祸因恶积,福缘善庆。
尺璧非宝,寸阴是竞。
资父事君,曰严与敬。
孝当竭力,忠则尽命。
临深履薄,夙兴温凊。
似兰斯馨,如松之盛。
川流不息,渊澄取映。
容止若思,言辞安定。
笃初诚美,慎终宜令。
荣业所基,籍甚无竟。
学优登仕,摄职从政。
存以甘棠,去而益咏。
乐殊贵贱,礼别尊卑。
上和下睦,夫唱妇随。
外受傅训,入奉母仪。
诸姑伯叔,犹子比儿。
孔怀兄弟,同气连枝。
交友投分,切磨箴规。
仁慈隐恻,造次弗离。
节义廉退,颠沛匪亏。
性静情逸,心动神疲。
守真志满,逐物意移。
坚持雅操,好爵自縻。
都邑华夏,东西二京。
背邙面洛,浮渭据泾。
宫殿盘郁,楼观飞惊。
图写禽兽,画彩仙灵。
丙舍旁启,甲帐对楹。
肆筵设席,鼓瑟吹笙。
升阶纳陛,弁转疑星。
右通广内,左达承明。
既集坟典,亦聚群英。
杜稿钟隶,漆书壁经。
府罗将相,路侠槐卿。
户封八县,家给千兵。
高冠陪辇,驱毂振缨。
世禄侈富,车驾肥轻。
策功茂实,勒碑刻铭。
磻溪伊尹,佐时阿衡。
奄宅曲阜,微旦孰营。
桓公匡合,济弱扶倾。
绮回汉惠,说感武丁。
俊义密勿,多士实宁。
晋楚更霸,赵魏困横。
假途灭虢,践土会盟。
何遵约法,韩弊烦刑。
起翦颇牧,用军最精。
宣威沙漠,驰誉丹青。
九州禹迹,百郡秦并。
岳宗泰岱,禅主云亭。
雁门紫塞,鸡田赤城。
昆池碣石,钜野洞庭。
旷远绵邈,岩岫杳冥。
治本于农,务兹稼穑。
俶载南亩,我艺黍稷。
税熟贡新,劝赏黜陟。
孟轲敦素,史鱼秉直。
庶几中庸,劳谦谨敕。
聆音察理,鉴貌辨色。
贻厥嘉猷,勉其祗植。
省躬讥诫,宠增抗极。
殆辱近耻,林皋幸即。
两疏见机,解组谁逼。
索居闲处,沉默寂寥。
求古寻论,散虑逍遥。
欣奏累遣,戚谢欢招。
渠荷的历,园莽抽条。
枇杷晚翠,梧桐蚤凋。
陈根委翳,落叶飘摇。
游鹍独运,凌摩绛霄。
耽读玩市,寓目囊箱。
易輶攸畏,属耳垣墙。
具膳餐饭,适口充肠。
饱饫烹宰,饥厌糟糠。
亲戚故旧,老少异粮。
妾御绩纺,侍巾帷房。
纨扇圆洁,银烛炜煌。
昼眠夕寐,蓝笋象床。
弦歌酒宴,接杯举觞。
矫手顿足,悦豫且康。
嫡后嗣续,祭祀烝尝。
稽颡再拜,悚惧恐惶。
笺牒简要,顾答审详。
骸垢想浴,执热愿凉。
驴骡犊特,骇跃超骧。
诛斩贼盗,捕获叛亡。
布射僚丸,嵇琴阮啸。
恬笔伦纸,钧巧任钓。
释纷利俗,并皆佳妙。
毛施淑姿,工颦妍笑。
年矢每催,曦晖朗曜。
璇玑悬斡,晦魄环照。
指薪修祜,永绥吉劭。
矩步引领,俯仰廊庙。
束带矜庄,徘徊瞻眺。
孤陋寡闻,愚蒙等诮。
谓语助者,焉哉乎也。

阅读更多...

Golang runtime.Gosched()函数浅析

分类:技术文档 - Golang | 阅读(734) | 发布于:2019-05-08 10:39 | 标签:无

以下是官方的定义:
// Gosched yields the processor, allowing other goroutines to run. It does not
// suspend the current goroutine, so execution resumes automatically.
func Gosched() {
    mcall(gosched_m)
}

这个函数的作用是让当前goroutine让出CPU,好让其它的goroutine获得执行的机会。同时,当前的goroutine也会在未来的某个时间点继续运行。
请看下面这个例子

package main
import (
	"fmt"
	//"runtime"
)
func say(s string) {
	for i := 0; i < 2; i++ {
		//runtime.Gosched()
		fmt.Println(s, i)
	}
}
func main() {
	go say("world")
	say("hello")
}
//执行输出:
//hello 0
//hello 1

---------取消注释runtime.Gosched()---------

package main
import (
	"fmt"
	"runtime"
)
func say(s string) {
	for i := 0; i < 2; i++ {
		runtime.Gosched()
		fmt.Println(s, i)
	}
}
func main() {
	go say("world")
	say("hello")
}
//执行输出:
//hello 0
//world 0
//hello 1

可以看到使用runtime.Gosched()后,先输出"hello 0",再输出"world 0",再输出"hello 1",最后一个"world 1"没有机会输出线程就结束了。

再看下面这个例子:

package main
import "fmt"
func showNumber (i int) {
	fmt.Println(i)
}
func main() {
	for i := 0; i < 10; i++ {
		go showNumber(i)
	}
	fmt.Println("Haha")
}
//执行输出:
//Haha

---------使用runtime.Gosched()---------

package main
import (
	"fmt"
	"runtime"
)
func showNumber(i int) {
	fmt.Println(i)
}
func main() {
	for i := 0; i < 10; i++ {
		go showNumber(i)
	}
	runtime.Gosched()
	fmt.Println("Haha")
}
//执行输出:(每次结果不一定,但"Haha"一定输出且在最后)
//6
//3
//7
//1
//8
//9
//4
//0
//2
//5
//Haha

分析:默认情况goroutins都是在一个线程里执行的,多个goroutins之间轮流执行,当一个goroutine发生阻塞,Go会自动地把与该goroutine处于线程的其他goroutines转移到另一个线程上去,以使这些goroutines不阻塞。 通过上面的结果可以看出,主线程执行完毕之后程序就退出了。 最后,如果代码中通过 runtime.GOMAXPROCS(n) 其中n是整数,指定使用多核的话,多个goroutines之间就可以实现真正的并行,结果就会上面的有所不同。

阅读更多...

Go原子操作 sync/atomic

分类:技术文档 - Golang | 阅读(678) | 发布于:2019-05-07 13:37 | 标签:无

sync/atomic包提供了底层的原子级内存操作,其执行过程不能被中断,这也就保证了同一时刻一个线程的执行不会被其他线程中断,也保证了多线程下数据操作的一致性。
操作的数据类型共有六种:int32, int64, uint32, uint64, uintptr, unsafe.Pinter,每一种类型又提供了五种操作:Add增减, CompareAndSwap比较并交换, Swap交换, Load载入, Store存储。
函数名以"操作+类型"组合而来。例如AddInt32/AddUint64/LoadInt32/LoadUint64...

以Int32为例:

// AddInt32 atomically adds delta to *addr and returns the new value.
func AddInt32(addr *int32, delta int32) (new int32)
AddInt32可以实现对元素的原子增加或减少,函数会直接在传递的地址上进行修改操作。
addr需要修改的变量的地址,delta修改的差值[正或负数],返回new修改之后的新值。

var a int32 
a += 10 
atomic.AddInt32(&a, 10) 
fmt.Println(a == 20) // true

//需要注意的是如果是uint32,unint64时,不能直接传负数,所以需要利用二进制补码机制
var b uint32
b += 20 
atomic.AddUint32(&b, ^uint32(10-1)) // 等价于 b -= 10 
// atomic.Adduint32(&b, ^uint32(N-1)) //N为需要减少的正整数值
fmt.Println(b == 10) // true

// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
函数会先判断参数addr指向的值与参数old是否相等,如果相等,则用参数new替换参数addr的值。最后返回swapped是否替换成功。

package main
import (
	"fmt"
	"sync"
	"sync/atomic"
)
func main() {
	var c int32
	wg := sync.WaitGroup{}
	//开启100个goroutine
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			tmp := atomic.LoadInt32(&c)
			if !atomic.CompareAndSwapInt32(&c, tmp, tmp+1) {
				fmt.Println("c 修改失败")
			}
		}()
	}
	wg.Wait()
	//c的值有可能不等于100,频繁修改变量值情况下,CompareAndSwap操作有可能不成功。
	fmt.Println("c : ", c)
}
偶尔输出:
//c 修改失败
//c :  99
多数情况下输出
//c :  100

// SwapInt32 atomically stores new into *addr and returns the previous *addr value.
func SwapInt32(addr *int32, new int32) (old int32)
Swap会直接执行赋值操作,并将原值作为返回值返回

package main
import (
	"fmt"
	"sync"
	"sync/atomic"
)
func main() {
	var e int32
	wg2 := sync.WaitGroup{}
	//开启10个goroutine
	for i := 0; i < 10; i++ {
		wg2.Add(1)
		go func() {
			defer wg2.Done()
			tmp := atomic.LoadInt32(&e)
			old := atomic.SwapInt32(&e, tmp + 1)
			fmt.Println("e old : ", old)
		}()
	}
	wg2.Wait()
	fmt.Println("e : ", e)
}

// e old :  3
// e old :  1
// e old :  2
// e old :  4
// e old :  5
// e old :  6
// e old :  7
// e old :  8
// e old :  9
// e old :  0
// e :  10

// LoadInt32 atomically loads *addr.
func LoadInt32(addr *int32) (val int32)
Load函数参数为需要读取的变量地址,返回值为读取的值
Load和Store操作对应与变量的原子性读写,许多变量的读写无法在一个时钟周期内完成,而此时执行可能会被调度到其他线程,无法保证并发安全。Load只保证读取的不是正在写入的值,Store只保证写入是原子操作。所以在使用的时候要注意。


// StoreInt32 atomically stores val into *addr.
func StoreInt32(addr *int32, val int32)
Store函数参数为需要存储的变量地址及需要写入的值,存储某个值时,任何CPU都不会对该值进行读或写操作,存储操作总会成功,它不关心旧值是什么,与CompareAndSwap不同

var d int32
fmt.Println("d : ", d)
atomic.StoreInt32(&d, 666)
fmt.Println("d : ", d)

阅读更多...

golang mysql select * 优化

分类:技术文档 - Golang | 阅读(1002) | 发布于:2019-04-18 16:45 | 标签:无

初学者使用golang mysql做查询时,一般直接使用原生的select * from table来查询数据:
type UserInfo struct {
    Id   int64
    Name string
    Sex  string
    //...
}

func GetUserInfo(uid int64) (info UserInfo, err error) {
    Db := lib.MysqlConn()
    sql := fmt.Sprintf("select * from user where uid=%d", uid)
    rows, err := Db.Query(sql)
    if err != nil {
        return info, err
    }
    defer Db.Close()

    if rows.Next() {
        var (
            id   int64
            name string
            sex  string
            //...
        )
        //得一个个的字段罗列出来
        err = rows.Scan(&id, &name, &sex, //...)
        info = UserInfo{
            Id:   id,
            Name: name,
            Sex:  sex,
            //...
        }
    }

    return info, nil
}
后来数据表结构变动直接导致GetUserInfo()报错了,因为字段对应不上了。然后优化了一版:
func GetUserInfo(uid int64) (info UserInfo, err error) {
    Db := lib.MysqlConn()
    sql := fmt.Sprintf("select `id`,`user_name`,`sex`,... from user where uid=%d", uid)
    rows, err := Db.Query(sql)
    if err != nil {
        return
    }
    defer Db.Close()

    if rows.Next() {
        //得一个个的字段罗列出来
        err = rows.Scan(&info.Id, &info.Name, &info.Sex, //...)
        if err != nil {
            return
        }
    }

    return
}
此时问题解决了,然后继续写类似的代码。
时间久了,就发现这样写好累,每次都得把所有的字段都罗列出来,而且上下一一对应。
所以就想能不能自动生成上下对应的变量,然后直接用变量来替换上。
如是,就用到了golang的反射,先定义一个结构体和数据表的字段一一对应,并在字段说明里面定义数据库的字段名:
type UserInfo struct {
	Id   int64  `sql:"id"`
	Name string `sql:"user_name"`
	Sex  string `sql:"sex"`
	//...
}
可以使用反射提取出类似`sql:"id"`里面id,然后拼装起来,具体如下
func (ui *UserInfo) allFields() (sqlFields string) {
	arr := []string{}
	el := reflect.TypeOf(ui).Elem()
	for i := 0; i < el.NumField(); i++ {
		arr = append(arr, el.Field(i).Tag.Get("sql"))
	}

	sqlFields = "`" + strings.Join(arr, "`,`") + "`"
	return
}
所以上面的函数中sql语句的定义就改为了:
//...
sql := fmt.Sprintf("select "+info.allFields()+" from user where uid=%d", uid)
//...
问题又来了,
rows.Scan(&info.Id, &info.Name, &info.Sex, //...)
rows.Scan怎么优化?
这里实际上就是每个字段的地址,如果取出该变量的地址,组成一个切片,然后展开切片就可以了,再来定义方法:
func (ui *UserInfo) allValues() (sqlValues []interface{}) {
	vl := reflect.ValueOf(ui).Elem()
	num := reflect.TypeOf(ui).Elem().NumField()
	for i := 0; i < num; i++ {
		sqlValues = append(sqlValues, vl.Field(i).Addr().Interface())
	}
	return
}

func GetUserInfo(uid int64) (info UserInfo, err error) {
	Db := lib.MysqlConn()
	sql := fmt.Sprintf("select "+info.allFields()+" from user where uid=%d", uid)
	rows, err := Db.Query(sql)
	if err != nil {
		return
	}
	defer Db.Close()

	if rows.Next() {
		//获取每个字段的切片
		fields := info.allValues()
		err = rows.Scan(fields...)
		if err != nil {
			return
		}
	}

	return
}
把 info.allFields()和info.allValues()合并在一起返回:
type UserInfo struct {
	Id   int64  `sql:"id"`
	Name string `sql:"user_name"`
	Sex  string `sql:"sex"`
	//...
}

func (ui *UserInfo) allFieldsAndValues() (sqlFields string, sqlValues []interface{}) {
	arr := []string{}
	el := reflect.TypeOf(ui).Elem()
	vl := reflect.ValueOf(ui).Elem()
	for i := 0; i < el.NumField(); i++ {
		arr = append(arr, el.Field(i).Tag.Get("sql"))
		sqlValues = append(sqlValues, vl.Field(i).Addr().Interface())
	}

	sqlFields = "`" + strings.Join(arr, "`,`") + "`"

	return
}

func GetUserInfo(uid int64) (info UserInfo, err error) {
	Db := lib.MysqlConn()

	sqlFields, sqlValues := info.allFieldsAndValues()
	sql := fmt.Sprintf("select "+sqlFields+" from user where uid=%d", uid)
	rows, err := Db.Query(sql)
	if err != nil {
		return
	}
	defer Db.Close()

	if rows.Next() {
		err = rows.Scan(sqlValues...)
	}

	return
}
现在封装起来后就方便多了,不担心数据表结构变动:这里只需要修改结构体UserInfo的信息。
现在又有一个方法需要批量的获取UserInfo,方法UserInfo.allFieldsAndValues()就大有用处了:
func GetAllUserInfo() (all []UserInfo, err error) {
	Db := lib.MysqlConn()
	info := UserInfo{}
	sqlFields, sqlValues := info.allFieldsAndValues()
	rows, err := Db.Query("select " + sqlFields + " from user ")
	if err != nil {
		return
	}
	defer Db.Close()

	for rows.Next() {
		err = rows.Scan(sqlValues...)
		if err != nil {
			return
		}
		all = append(all, info)
		info = UserInfo{}
	}

	return
}
这里在循环内[for rows.Next(){//...}]因为结构体的传值是值传递,all = append(all, info)操作的是info的一个拷贝。
用完之后在把变量info的值给重置掉:info = UserInfo{}。最后返回all。

Go语言中有的传参是值传递(传值),是一个副本,一个拷贝。
因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中就无法修改原内容数据;
有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据

阅读更多...

汉语的精粹

分类:散文阅读 - 科普阅读 | 阅读(636) | 发布于:2019-04-09 12:50 | 标签:无

1.中国就足球和乒乓球的赛事最无聊,一个看都不用看,另一个看都不用看;一个谁都打不过,另一个谁都打不过。

2.单身的原因:原来是喜欢一个人,现在是喜欢一个人。

3.冬天:能穿多少穿多少; 夏天:能穿多少穿多少。

阅读更多...

golang实现一个简单的FTP服务器

分类:技术文档 - Golang | 阅读(1274) | 发布于:2019-04-02 14:53 | 标签:无

使用golang实现一个简单的ftp服务器,方便传文件

使用:
ftpd.exe [-user=admin] [-pass=123456] [-root=C:\www] [-host=0.0.0.0] [-post=21]
可双击打开直接运行,即使用默认配置.
默认 用户名:admin (-user=admin)
密码:123456 (-pass=123456)
根目录:(-root=当前执行文件所在目录)

下载Windows版本:ftpd.exe

源码:(运行后程序会自动生成 code/main.go 的源文件)

package main

import (
	"flag"
	"log"

	"github.com/goftp/file-driver"
	"github.com/goftp/server"
	"path/filepath"
	"os"
	"strings"
	"fmt"
)

func init() {

	/*
	err := asset.RestoreAssets("./", "code")
	if err != nil {
		fmt.Println("RestoreAssets code error", "\n", err, "\n")
	}
	*/

	fmt.Println("to build exe file usage:")
	fmt.Println("go-bindata -o asset/bindata.go -pkg asset code")
	fmt.Println(`go build -o ftpd.exe code\main.go`)
	fmt.Println()
}

func main() {
	var (
		root = flag.String("root", getCurrentDirectory(), "Root directory to serve")
		user = flag.String("user", "admin", "Username for login")
		pass = flag.String("pass", "123456", "Password for login")
		port = flag.Int("port", 21, "Port")
		host = flag.String("host", "0.0.0.0", "Port")
	)
	flag.Parse()

	if *root == "" {
		*root = getCurrentDirectory()
	}

	factory := &filedriver.FileDriverFactory{
		RootPath: *root,
		Perm:     server.NewSimplePerm("user", "group"),
	}

	opts := &server.ServerOpts{
		Factory:  factory,
		Port:     *port,
		Hostname: *host,
		Auth:     &server.SimpleAuth{Name: *user, Password: *pass},
	}

	log.Printf("Starting ftp server on \n"+
		"Host      %v \n"+
		"Port      %v \n"+
		"Username  %v \n"+
		"Password  %v \n"+
		"RootDir   %s \n\n",
		opts.Hostname, opts.Port, *user, *pass, factory.RootPath)

	ftpServer := server.NewServer(opts)
	err := ftpServer.ListenAndServe()
	if err != nil {
		log.Fatal("Error starting server:", err)
	}
}

func getCurrentDirectory() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}
	return strings.Replace(dir, "\\", "/", -1)
}

阅读更多...

golang打包封装静态资源 go-bindata

分类:技术文档 - Golang | 阅读(1647) | 发布于:2019-04-01 20:55 | 标签:无

使用 Go 开发应用的时候,有时会遇到需要读取静态资源的情况。 比如开发 Web 应用,程序需要加载模板文件生成输出的 HTML。 在程序部署的时候,除了发布应用可执行文件外,还需要发布依赖的静态资源文件。这给发布过程添加了一些麻烦。 既然发布单独一个可执行文件是非常简单的操作,就有人会想办法把静态资源文件打包进 Go 的程序文件中。
下面就来看一个解决方案:go-bindata

go-bindata 可以把多个静态文件内容转码嵌入到一个 go 文件中,并提供一些操作方法。

安装 go-bindata :

go get -u github.com/jteeuwen/go-bindata/...
注意 go get 地址最后的三个点 ...。这样会分析所有子目录并下载依赖编译子目录内容。go-bindata 的命令工具在子目录中。

使用 go-bindata :

go-bindata -o=app/asset/asset.go -pkg=asset source/... theme/... doc/source/... doc/theme/... 
-o 输出文件到 app/asset/asset.go,包名 -pkg=asset,然后是需要打包的目录,三个点包括所有子目录。
这样就可以把所有相关文件打包到asset包的asset.go文件中,且开头是 package asset 保持和目录一致。

项目中释放静态文件的代码 :

dirs := []string{"source", "theme", "doc"} // 设置需要释放的目录

for _, dir := range dirs {
    // 解压dir目录到当前目录
    if err := asset.RestoreAssets("./", dir); err != nil {
        fmt.Println("RestoreAssets ", dir, "error", "\n", err)
    }
}
asset.go 内的静态内容还是根据实际的目录位置索引。所以我们可以直接通过目录或者文件地址去操作。

遇到的坑:

1. 安装时没有权限 : Windows上运行没有权限的使用管理员身份运行
2. go-bindata找不到路径: 记得把 $GOPATH/bin 加入系统 PATH
3. 生成的压缩包文件太大,goland编辑器加载出错:[the file size(10M) exceeds configured limit(4M).Code insight features are not available] :重新设置编辑器IDE的idea.max.intellisense.filesize值:(idea.max.intellisense.filesize=10240)
4. 解压操作[RestoreAssets]可以放到init()的函数中,项目启动后优先启动。
5. 流程:先使用go-bindata打包静态资源目录及文件[go-bindata -o=asset\asset.go -pkg=asset webServer/...],然后使用go build命令生成二进制可执行文件,最后把该可执行文件复制到其运行目录就可以直接运行而不丢失静态资源文件。
6. go-bindata命令必须指定一个"-pkg asset",并且"-o asset/asset.go"? 此处的pkg不能直接使用main吗,直接放生成main包里面的文件并且放入main包后 代码内调用RestoreAssets函数报错。。。

阅读更多...

小巧HTTP静态资源服务器

分类:技术文档 - Golang | 阅读(705) | 发布于:2019-03-04 17:06 | 标签:无

工作中难免遇到使用http去传输一些文件,为了避免每次去下载安装一些软件,自己使用golang快速实现一个。双击打开,填入端口号及路径,直接访问浏览器“http://IP:端口”号即可浏览文件

代码如下:(后面提供windows版本下载地址)

package main

import (
	"os"
	"fmt"
	"time"
	"strings"
	"net/http"
	"path/filepath"
)

func main() {

	//设定默认值
	defaultPort, defaultDir := "8080", getCurrentDirectory()
	port, dir := "", ""

	if len(os.Args) > 1 {
		//使用命令行直接指定参数
		for k, v := range os.Args {
			if k == 0 {
				continue
			}

			if v == "" {
				continue
			}

			val := strings.Split(v, "=")

			if strings.ToLower(val[0]) == "-port" {
				port = strings.ToLower(val[1])
				continue
			}

			if strings.ToLower(val[0]) == "-dir" {
				dir = strings.ToLower(val[1])
				continue
			}
		}
	}

	//Usage
	if len(os.Args) < 3 || port == "" || dir == "" {
		fmt.Println("Usage: " + os.Args[0] + " [-port=?] [-dir=?] ")
	}

	//使用命令交互模式指定参数port
	if port == "" {
		fmt.Println("Please input http port [default is \"" + defaultPort + "\"] :")
		fmt.Scanln(&port)
		if port == "" {
			port = defaultPort
		}
	}

	//使用命令交互模式指定参数dir
	if dir == "" {
		fmt.Println("Please input http static resource directory [default is \"" + defaultDir + "\"] :")
		fmt.Scanln(&dir)
		if dir == "" {
			dir = defaultDir
		}
	}

	fmt.Println("HttpServer is running at port:" + port + " directory:" + dir)
	fmt.Println("Input \"Ctrl+C\" to stop it.")
	http.Handle("/", http.FileServer(http.Dir(dir)))
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		fmt.Println(err)
		time.Sleep(time.Duration(5) * time.Second)
	}
}

//获取当前程序所在目录
func getCurrentDirectory() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		return "./"
	}
	return strings.Replace(dir, "\\", "/", -1)
}

自己编译命令:

go build -o fileServer.exe fileServer.go

直接下载Windows版本:fileServer.exe

阅读更多...

golang http 路由的轻量实现

分类:技术文档 - Golang | 阅读(751) | 发布于:2019-02-28 17:30 | 标签:无

package main

import (
	"net/http"
	"fmt"
)

type Application struct {
	Route map[string]map[string]http.HandlerFunc
}

func NewApplication(route map[string]map[string]http.HandlerFunc) *Application{
	return &Application{Route:route}
}

//路由表初始化/注册路由
func (app *Application) HandleFunc(method, path string, f http.HandlerFunc) {
	if app.Route == nil {
		app.Route = make(map[string]map[string]http.HandlerFunc)
	}
	if app.Route[method] == nil {
		app.Route[method] = make(map[string]http.HandlerFunc)
	}
	app.Route[method][path] = f
}

//实现路由核心处理ServeHTTP方法
func (app *Application) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	//写log
	fmt.Println(req.Method, req.URL.Path)

	//路由执行
	if f, ok := app.Route[req.Method][req.URL.Path]; ok {
		f(res, req)
	} else {
		res.WriteHeader(404)
		fmt.Fprintf(res, "404 Page Not Found")
	}
}

//Get路由注册
func (app *Application) Get(path string, HandlerFunc http.HandlerFunc) {
	app.HandleFunc("GET", path, HandlerFunc)
}

//Post路由注册
func (app *Application) Post(path string, HandlerFunc http.HandlerFunc) {
	app.HandleFunc("POST", path, HandlerFunc)
}

//启动Http
func (app *Application) Run(addr string) error {
	return http.ListenAndServe(addr, app)
}

//Get操作
func IndexHandel(res http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(res, "Index Page")
}

//Get操作
func HomePageHandel(res http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(res, "Show Homepage")
}

//Post操作
func EditHomePageHandel(res http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(res, "Edit Homepage")
}

func main() {
	app := NewApplication(nil)
	app.Get("/", IndexHandel)
	app.Get("/home", HomePageHandel)
	app.Post("/home", EditHomePageHandel)

	err := app.Run(":8080")
	if err != nil {
		fmt.Println(err.Error())
	}
}

阅读更多...

php数据压缩函数gzcompress使用

分类:技术文档 - PHP文档 | 阅读(974) | 发布于:2018-11-05 19:22 | 标签:无

压缩:gzcompress 解压:gzuncompress

 123,
    'name' => '张三',
    'age' => 24
];

$json = json_encode($data, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
$compressed= gzcompress($json);
$mysql_insert_data = base64_encode($compressed);

$compressed_new = base64_decode($mysql_insert_data);
$json_new = gzuncompress($compressed_new);
$data_new = json_decode($json_new, true);

print_r($data_new);

1.如果要json_encode的话,要先于gzcompress执行。如果gzcompress先执行,json_encode返回的是空值。
2.gzcompress(json_encode(数组))这种写法是不对的,得到的结果是一堆乱码。必须分开写,json_encode处理结果赋值给一个变量,然后gzcompress处理这一变量
3.gzcompress结果直接存入数据库不会成功。可以base64_encode一下。

经研究发现, gzuncompress的处理结果与zlib_decode的处理结果相同. (在gzcompress时使用了zlib格式). 把gzuncompress函数,换成zlib_decode即可. 完全不影响使用.

阅读更多...