说明
之前要写一个项目文档,需要给上级审核之后才能push到线上。直接扔markdown文件有些不合适,但将它转为pdf文件发送有点麻烦,每次修改都需要转一次,再发送。因此就有了写一个简单的markdown在线解析服务的想法。在本地搭一个服务,到时候直接给地址,意见反馈后,直接本地修改,服务更新后就能看到新版本了。
dependences
- github.com/microcosm-cc/bluemonday
- gopkg.in/russross/blackfriday.v2
文件目录
- ./markdowns/* 存放需要解析的markdown文件
- ./templates/* 存放工程需要的html模板
接口
- / index显示扫描到的文件列表
- /read?file=${fileName} 显示markdown转换后的HTML页面
- /update 调用后,重新扫描文件目录
基本思路
简单的在main.go里增加一个全局以文件名称为key,具体路径为value的map。index页面就遍历这个map,read接口就从这里获取文件路径,读取返回。update也是更新这个表。后期如果增加缓存,将路径string改为自定义的struck。
实现
index.html模板
Document markdownlist
{ {range $key, $value := .}} { {$key}} { {end}}
main.go
package mainimport ( "fmt" "html/template" "io/ioutil" "net/http" "os" "path/filepath" "strings" "sync" "github.com/microcosm-cc/bluemonday" "gopkg.in/russross/blackfriday.v2")var ( MARK_DOWNS_PATH = "markdowns" TEMPLATES_PATH = "templates" fileMap map[string]string globalLock *sync.RWMutex)func init() { globalLock = new(sync.RWMutex) updateMarkdownFileList()}func updateMarkdownFileList() { globalLock.Lock() defer globalLock.Unlock() fileMap = make(map[string]string) files, _ := filepath.Glob(fmt.Sprintf("%s/*", MARK_DOWNS_PATH)) for _, f := range files { fileName := f[strings.LastIndex(f, string(os.PathSeparator))+1 : len(f)-3] fileMap[fileName] = f }}func readMarkdownFile(fileName string) ([]byte, error) { globalLock.RLock() defer globalLock.RUnlock() markdownFile, ok := fileMap[fileName] if !ok { return nil, fmt.Errorf("file(%s)NotExist", fileName) } if fileread, err := ioutil.ReadFile(markdownFile); err == nil { unsafe := blackfriday.Run(fileread) html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) return html, nil } else { return nil, fmt.Errorf("file(%s)ReadFail", fileName) }}func indexHandler(w http.ResponseWriter, r *http.Request) { t := template.New("index.html") t, _ = t.ParseFiles(fmt.Sprintf("%s%sindex.html", TEMPLATES_PATH, string(os.PathSeparator))) t.Execute(w, fileMap)}func readerHander(w http.ResponseWriter, r *http.Request) { name := r.URL.Query().Get("file") body, err := readMarkdownFile(name) if err != nil { w.WriteHeader(http.StatusNotFound) } else { w.Write(body) }}func updateHandler(w http.ResponseWriter, req *http.Request) { updateMarkdownFileList() w.Write([]byte("success"))}func main() { http.HandleFunc("/", indexHandler) http.HandleFunc("/read", readerHander) http.HandleFunc("/update", updateHandler) http.ListenAndServe(":8000", nil)}
小结
项目的重点就在于markdown解析成html,既然都直接使用现成的第三方包,就基本没有什么技术性。唯一需要考虑的就是锁的问题。后期还可以增加缓存来提高性能,增加trylock限制update接口更新的问题。
ennn
以下是新版本pro的代码,主要是增加了一些锁增强并发和易用性的东西,与功能无大关系