新自动化文档生成-go语言-docx生成


关键字: word go语言 办公软件 自动化 excel office

背景

近来因为工作需要,经常性要重复地写报告,搞安全的就是苦逼啊。每次都需要人工查这查那,还要复制黏贴。 重复地复制黏贴会比较烦人。所以弄好一份表就自动让机器代替人工黏贴到word文档,自动生成会相对舒服且快速,有什么问题直接修改excel即可。

ps:因为苦于unioffice库的问题,所以重新找了两个库来实现,发现生成后的文件小了很多,而且文档及库的使用极度方便。

步骤

我是用go语言进行开发的。
go的版本:go version go1.14.4 windows/amd64

github.com/360EntSecGroup-Skylar/excelize/v2
github.com/nguyenthenguyen/docx

命令进行安装:
go get -v github.com/xxx

ps:
这两个库相对简单,excel的手册地址:https://xuri.me/excelize/zh-hans/workbook.html#SetDocProps
docx就暂时没有手册,但是提供的函数不多。研究一下即可。
注意两个函数:ReplaceRaw和Replace ,这两个的区别是ReplaceRaw直接动docx底层文件xml;Replace只是把你想要替换的内容给替换。

确定流程

其实最好先确定一下程序的处理流程和具体人工处理的步骤,因为偷懒我这里就不提供了,因为我也是画着画着就没有弄了。
该项目的步骤:
1、先填写好一个excel表,作为程序的数据输入。
2、打开模板docx,程序处理,输出修改后的报告。
3、输出写好的数据到一个汇总的表进行统计和闭环。

编写伪代码

在vscode上编写具体的流程伪代码

编写正式的代码

按照自己的思路,确定好变量、常量、具体的处理流程等等,按照伪代码进行一步一步的代码编写。

最终的代码

package main

import (
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/360EntSecGroup-Skylar/excelize/v2"
	"github.com/nguyenthenguyen/docx"
)

type biaozhi struct {
	yeMeiTime  string // 页眉的时间编号
	neiRouTime string // 内容的时间格式

	sjmc   string   // 事件名称
	sjjb   string   // 事件级别(漏洞、风险、应急)
	ipaddr string   // 告警对象(应急)
	gjlj   []string // 告警路径(应急、风险)
	tms    string   // 告警数量(应急)
	gjxx   string   // 告警描述(漏洞、风险)
	czjy   []string // 处置建议(漏洞、风险)
	sjly   string   // 事件类型(应急)
	llmc   string   // 漏洞名称(漏洞)
	yxbb   []string // 影响版本(漏洞)
	ywxt   string   // 业务系统(应急、风险)
	ywdw   string   // 业务单位(漏洞、应急、风险)
	fxms   string   // 风险验证描述(风险)
	kyfx   string   // 可疑分析(可疑)

	czjyStr   string // 写入excel的处置建立
	lujingStr string // 写入excel中的路径
	lcw       string // 另存为文件的名称
}

var (
	// Baogao 要打开的报告类型
	Baogao string
	// SaveFile 要保存的报告类型
	SaveFile string
	dataList biaozhi
	// qiantou这个比较重要。
	/*
	我不知道什么原因,这两个参数我已经第二次修改了,可能是因为docx的问题,所以建议如果你要动程序之前,最好先自己测试一下docx的这两个参数对不对。直接解压docx看就可以了。
	*/
	qiantou string = `<w:p><w:pPr><w:rPr><w:rFonts w:hint="eastAsia" w:ascii="仿宋_GB2312" w:hAnsi="仿宋_GB2312" w:eastAsia="仿宋_GB2312" w:cs="仿宋_GB2312"/><w:sz w:val="24"/><w:szCs w:val="24"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:hint="eastAsia" w:ascii="仿宋_GB2312" w:hAnsi="仿宋_GB2312" w:eastAsia="仿宋_GB2312" w:cs="仿宋_GB2312"/><w:sz w:val="24"/><w:szCs w:val="24"/><w:lang w:val="en-US" w:eastAsia="zh-CN"/></w:rPr><w:t>`
	jiewei  string = `</w:t></w:r><w:bookmarkStart w:id="0" w:name="_GoBack"/><w:bookmarkEnd w:id="0"/></w:p>`
)

// 1.判断输出的是什么通告
func tonggao() {
	cl, err := excelize.OpenFile("素材.xlsx")
	if err != nil {
		log.Println("打开素材文件错误!")
		return
	}
	SaveFile, _ = cl.GetCellValue("需要填写的资料", "B1")
	switch SaveFile {
	case "可疑行为":
		Baogao = "安全可疑行为通告模板.docx"
	case "风险预警":
		Baogao = "安全风险预警通告模板.docx"
	case "应急预警":
		Baogao = "安全事件应急通告模板.docx"
	case "漏洞预警":
		Baogao = "安全漏洞预警通告模板.docx"
	default:
		log.Println("请检查(素材.xlsx)文件(需要填写的资料)表中报告类型是否正确!")
	}

	// 获取相关的数据
	dataList.事件名称, _ = cl.GetCellValue("需要填写的资料", "B2")   // 风险预警:获取事件名称
	dataList.sjjb, _ = cl.GetCellValue("需要填写的资料", "B3")   // 所有预警:获取事件级别
	dataList.ipaddr, _ = cl.GetCellValue("需要填写的资料", "B4") // 应急预警:告警对象
	gjlj, _ := cl.GetCellValue("需要填写的资料", "B5")
	dataList.gjlj = strings.Split(gjlj, "\n")           // 告警路径
	dataList.tms, _ = cl.GetCellValue("需要填写的资料", "B6")  // 告警数量
	dataList.gjxx, _ = cl.GetCellValue("需要填写的资料", "B7") // 告警描述
	dataList.czjyStr, _ = cl.GetCellValue("需要填写的资料", "B8")
	dataList.czjy = strings.Split(dataList.czjyStr, "\n") // 所有预警:获取处置建议
	dataList.sjly, _ = cl.GetCellValue("需要填写的资料", "B9")   // 应急预警:事件类型
	dataList.llmc, _ = cl.GetCellValue("需要填写的资料", "B10")  // 漏洞预警:漏洞名称
	yxbb, _ := cl.GetCellValue("需要填写的资料", "B11")
	dataList.yxbb = strings.Split(yxbb, "\n")            // 漏洞预警:影响版本
	dataList.ywxt, _ = cl.GetCellValue("需要填写的资料", "B12") // 风险预警:业务系统名称
	dataList.ywdw, _ = cl.GetCellValue("需要填写的资料", "B13") // 风险预警:业务单位名称
	dataList.fxms, _ = cl.GetCellValue("需要填写的资料", "B14") // 风险预警:风险验证描述
	dataList.kyfx, _ = cl.GetCellValue("需要填写的资料", "B15") // 可疑行为:可疑分析

}

// 2.处理并输出报告
func baogao() {
	// 打开一个已有格式的文档,这个是要打开的文档路径。
	filesDir := "./template/" + Baogao
	// 添加页眉
	r, err := docx.ReadDocxFile(filesDir)
	if err != nil {
		log.Printf("打开word文档错误,错误信息: %s", err)
	}
	log.Println("打开报告完成")
	defer r.Close()
	docx1 := r.Editable()
	docx1.ReplaceHeader("页眉日期", dataList.yeMeiTime)

	switch SaveFile {
	case "应急预警":
		log.Println("当前正在处理应急预警类型的报告")
		docx1.Replace("事件名称:", "事件名称:"+dataList.sjmc, -1)
		docx1.Replace("事件类型:", "事件类型:"+dataList.sjly, -1)
		docx1.Replace("事件级别:", "事件级别:"+dataList.sjjb, -1)
		docx1.Replace("告警对象:", "告警对象:"+dataList.ipaddr, -1)
		docx1.Replace("所属系统:", "所属系统:"+dataList.ywxt+"系统,隶属于"+dataList.ywdw, -1)
		docx1.Replace("告警内容:", "告警内容:"+dataList.gjxx, -1)
		docx1.Replace("应急描述", dataList.neiRouTime+",发现业务(IP:"+dataList.ipaddr+")产生"+dataList.tms+"条“"+dataList.sjmc+"”日志告警信息。经云主机资产确认,该主机为“"+dataList.ywxt+"系统,隶属于"+dataList.ywdw+"。", -1)
		// 告警路径切片写入
		var shuZu []string
		for _, t := range dataList.gjlj {
			t = strings.Replace(t, "<", "&lt;", -1)
			t = strings.Replace(t, ">", "&gt;", -1)
			shuZu = append(shuZu, t+jiewei+qiantou)
		}
		shuZu = append(shuZu, jiewei)
		st1 := strings.Join(shuZu, "")
		docx1.ReplaceRaw("告警路径", st1, -1)
		// 处置建议切片写入
		var shuZu2 []string
		for _, t := range dataList.czjy {
		// 加了转换验证解决特殊符号导致的错误问题。
			t = strings.Replace(t, "<", "&lt;", -1)
			t = strings.Replace(t, ">", "&gt;", -1)
			shuZu2 = append(shuZu2, t+jiewei+qiantou)
		}
		shuZu2 = append(shuZu, jiewei)
		st2 := strings.Join(shuZu2, "") //把切片装回string类型的字符串
		docx1.ReplaceRaw("应急处置建议", st2, -1)
	case "漏洞预警":
		log.Println("当前正在处理漏洞预警类型的报告")
		docx1.Replace("漏洞名称填写", dataList.llmc, -1)
		docx1.Replace("风险等级填写", dataList.sjjb, -1)
		docx1.Replace("风险描述填写", dataList.gjxx, -1)
		// 影响版本切片写入
		var shuZu []string
		for _, t := range dataList.yxbb {
			t = strings.Replace(t, "<", "&lt;", -1)
			t = strings.Replace(t, ">", "&gt;", -1)
			shuZu = append(shuZu, t+jiewei+qiantou)
			//shuZu = append(shuZu, t)
		}
		shuZu = append(shuZu, jiewei)
		st1 := strings.Join(shuZu, "")
		// ReplaceRaw该函数是直接修改底层的xml文件。而Replace只是单纯的替换你想替换的内容,不修改格式。
		docx1.ReplaceRaw("影响版本填写", st1, 1)

		// 处置建议切片写入
		var shuZu2 []string
		for _, t := range dataList.czjy {
			t = strings.Replace(t, "<", "&lt;", -1)
			t = strings.Replace(t, ">", "&gt;", -1)
			shuZu2 = append(shuZu2, t+jiewei+qiantou)
		}
		shuZu2 = append(shuZu, jiewei)
		st2 := strings.Join(shuZu2, "")
		docx1.ReplaceRaw("处置建议填写", st2, -1)
	case "可疑行为":
		log.Println("当前正在处理可疑行为类型的报告")
		docx1.Replace("行为概述填写", dataList.neiRouTime+",资产:"+dataList.ipaddr+"产生"+dataList.tms+"条"+dataList.sjmc+"日志告警信息。经云主机资产确认,该主机为“"+dataList.ywxt+"”系统,隶属于“"+dataList.ywdw+"”。", -1)
		docx1.Replace("疑点分析填写", dataList.kyfx, -1)
		// 处置建议切片写入
		var shuZu []string
		for _, t := range dataList.czjy {
			t = strings.Replace(t, "<", "&lt;", -1)
			t = strings.Replace(t, ">", "&gt;", -1)
			shuZu = append(shuZu, t+jiewei+qiantou)
		}
		shuZu = append(shuZu, jiewei)
		st2 := strings.Join(shuZu, "")
		docx1.ReplaceRaw("处置建议填写", st2, -1)
	default:
		log.Println("正在处理默认的表格内容")
		docx1.Replace("事件名称填写", dataList.sjmc, -1)
		docx1.Replace("风险等级填写", dataList.sjjb, -1)
		docx1.Replace("风险描述填写", dataList.gjxx, -1)
		docx1.Replace("业务所属", "经云主机资产确认,该主机为"+dataList.ywxt+"系统,隶属于"+dataList.ywdw, -1)
		docx1.Replace("风险验证描述", dataList.fxms, -1)
		// 风险验证路径切片写入
		var shuZu []string
		for _, t := range dataList.gjlj {
			t = strings.Replace(t, "<", "&lt;", -1)
			t = strings.Replace(t, ">", "&gt;", -1)
			shuZu = append(shuZu, t+jiewei+qiantou)
		}
		shuZu = append(shuZu, jiewei)
		st1 := strings.Join(shuZu, "")
		docx1.ReplaceRaw("风险验证路径", st1, -1)
		// 处置建议切片写入
		var shuZu2 []string
		for _, t := range dataList.czjy {
			t = strings.Replace(t, "<", "&lt;", -1)
			t = strings.Replace(t, ">", "&gt;", -1)
			shuZu2 = append(shuZu2, t+jiewei+qiantou)
		}
		shuZu2 = append(shuZu, jiewei)
		st2 := strings.Join(shuZu2, "")
		docx1.ReplaceRaw("处置建议填写", st2, -1)
	}

	switch SaveFile {
	case "可疑行为":
		dataList.lcw = "【" + dataList.sjmc + "】" + "(" + dataList.ywdw + ")" + "安全可疑行为预警通告" + dataList.yeMeiTime + ".docx"
	case "风险预警":
		dataList.lcw = "【" + dataList.sjmc + "】" + "(" + dataList.ywdw + ")" + "安全风险预警通告" + dataList.yeMeiTime + ".docx"
	case "应急预警":
		dataList.lcw = "【" + dataList.sjmc + "】" + "(" + dataList.ywdw + ")" + "安全事件应急通告" + dataList.yeMeiTime + ".docx"
	case "漏洞预警":
		dataList.lcw = "【" + dataList.llmc + "】" + "(" + dataList.ywdw + ")" + "安全漏洞预警通告" + dataList.yeMeiTime + ".docx"
	}

	sp := "./report/" + dataList.lcw
	err = docx1.WriteToFile(sp)
	if err != nil {
		os.Mkdir("./report", os.ModePerm)
		log.Println("当前目录中没有report报告文件夹,现在创建文件夹成功!!!")
		docx1.WriteToFile(sp)
		log.Println("保存docx文件成功!!!")
	}
	log.Println("保存docx文件成功!!!")
}

func exc() {
	f, err := excelize.OpenFile("预警通报汇总表.xlsx")
	if err != nil {
		log.Println(err.Error())
	}

	//告警函编号、业务单位、业务系统、告警状态、漏洞名称、告警类型、告警级别、告警对象、告警描述、应急处置方法、发生时间、结束时间、告警根治处置方法建议、安全处置联系人、支撑人员、修复方法描述、修复时间、修复验证描述、修复验证人、修复验证时间

	// 非应急预警的格式
	exStr := []string{dataList.yeMeiTime, dataList.ywdw, dataList.ywxt, "未修复", dataList.llmc, dataList.事件名称, dataList.sjjb, dataList.ipaddr, dataList.gjxx, "", dataList.neiRouTime, "", dataList.czjyStr, "", "xxx", "", "", "", dataList.ywdw, SaveFile}

	// 检查A列一千行,判断是否有值,如果有值就不写。没有值就整行填写记录
	for n1 := 1; n1 < 1000; n1++ {
		s1 := strconv.Itoa(n1)
		lie := "A" + s1
		// 获取当前表,固定表格的值
		cell, _ := f.GetCellValue("Sheet1", lie)
		if cell == "" { // 判断是否为空
			// 为空就提交单元格编号并跳出循环
			f.SetSheetRow("Sheet1", lie, &exStr)
			// celSave = lie
			break
		}
	}
	// 保存文件
	f.Save()
	log.Println("通告内容已经写入“预警通报汇总表.xlsx”表中!!!")
}

func main() {
	// 获取符合我们格式的时间。
	dataList.neiRouTime = time.Now().Format("2006年01月02日15时04分")
	dataList.yeMeiTime = time.Now().Format("200601021504")

	// 判断是什么报告类型,提取素材exels里面的内容
	tonggao()

	// 将获取到的数据写入word并保存文件
	baogao()

	// 将获取到的数据写入到汇总excel里面
	exc()

	// 等待时间查看日志
	time.Sleep(time.Minute * 999)

}


以前的久问题

如果使用了unioffice库会出现以下问题:
1.页眉其实有排版和字体问题,可惜时间太短用了一天时间写了这个程序,因为是新手所以大神勿喷。
2.word读取的时候存在问题,需要docx清除所有格式才能如你所愿地识别出来字段,举个有问题的例子:

以下是word文档的两个段:
	恶意告警
	攻击事件经过分析还存在其他问题,1:端口继续对外开放;2:漏洞未被修复。

如果这个文档有自己的格式如:仿宋字体;加粗;四号等等。。。
那么识别就会出现问题,识别结果如下:

恶意
告警
攻击事件
经过
分析还存在其它问题,
1
:端口继续对外开放;2:漏洞未被修复。

那么识别出来的字段就会有问题,不能按照我们关键字的方式进行识别并替换。
如我想识别出恶意告警,作为关键字,再替换为我想要的写的内容。但是这样的情况下就无法做了。

3.excel读出数据之后,可以读取成切片,变成数组,通过unioffice这个库的AddBreak()函数进行换行,继续写入内容。示例代码如下:(目前已经不用该库了。直接动xml文件)

for _, t := range dataList.chuzhijianyi { //重复执行
	r.AddText(t) //写入内容
	r.AddBreak() //换行
}

新的注意点

针对以上的旧问题,已经不再是问题了,通过新的库已经解决。

注意的点

模版的问题

这里要说一下,docx是基于xml格式的文本文档;使用压缩软件打开docx文件:
在这里插入图片描述
在这里插入图片描述
这个就是docx的内容,看到下面还有页眉页尾的文件。打开后的内容,关注的是关键字的连续,如图:
在这里插入图片描述
硬件性能这个关键字,我们可以拿来作为替换;要注意的是,有可能这个关键字不一定是连续的。不连续的情况下,要重新删除内容,重新写,如图:
在这里插入图片描述
再用压缩软件检查,是否已经连续。不然后面会无法替换原文档内容。

插入的内容

因为文档是docx,所以要使用doc库里面的ReplaceRaw函数,该函数插入的内容是不经过转换的。

  • 所以需要添加xml的前缀内容:<w:p><w:pPr><w:rPr><w:rFonts w:hint=“eastAsia” w:ascii=“仿宋_GB2312” w:hAnsi=“仿宋_GB2312” w:eastAsia=“仿宋_GB2312” w:cs=“仿宋_GB2312”/><w:sz w:val=“24”/><w:szCs w:val=“24”/><w:lang w:val=“en-US” w:eastAsia=“zh-CN”/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:hint=“eastAsia” w:ascii=“仿宋_GB2312” w:hAnsi=“仿宋_GB2312” w:eastAsia=“仿宋_GB2312” w:cs=“仿宋_GB2312”/><w:sz w:val=“24”/><w:szCs w:val=“24”/><w:lang w:val=“en-US” w:eastAsia=“zh-CN”/></w:rPr><w:t>
  • 后缀内容是:</w:t></w:r><w:bookmarkStart w:id=“0” w:name="_GoBack"/><w:bookmarkEnd w:id=“0”/></w:p>
    中间内容就是你想替换的内容啦。详情可以看我的源码。挺简单的。

特殊符号

在实践中,发现xml并是无脑地插入文本,特别是遇到符号的时候,
如果无脑代入,会在打开docx的时候报错,实际上,就是因为符号引起。
举个例子:
在这里插入图片描述
原因如下:

更正一下图片中的内容:“大于号这四个字符组成的 & g t ;” 小于号是这四个字符组成的"& l t ;"

在这里插入图片描述
以下这个图是正常能打开的docx截图:
在这里插入图片描述
正常的docx的xml如下:

与什么空格并没有什么关系。

在这里插入图片描述
在这里插入图片描述
PS:这个问题,目前已经找到折衷办法,使用strings.Replace()函数,把大小于号都替换就可以了。

  • 在 XML 中,有 5 个预定义的实体引用:
转义后的字符需要被转义的字符备注
&lt;<less than
&gt;>greater than
&amp;&ampersand
&apos;apostrophe
&quot;"quotation mark

注释:在 XML 中,只有字符 “<” 和 “&” 确实是非法的。大于号是合法的,但是用实体引用来代替它是一个好习惯。

总结

1、检查好自己的模版文档。
2、检查好输入输出的excel文档。
3、调试好程序的输出。
4、可以使用日志告警作为程序的接入、数据库表作为资产查询的输出。看个人喜欢。

至于图片的插入,目前需要用到的是unioffice库,我还没有研究过。

最终成果:
在这里插入图片描述

  • 0
    点赞
  • 7
    收藏
  • 打赏
    打赏
  • 5
    评论
第01章 课程介绍 1-1 导学.mp4 1-2 课程介绍.mp4 第02章 实战-“云存储”系统原型 2-1 “云存储”系统原型之简单文件上传服务架构说明.mp4 2-2 编码实战:“云存储”系统之实现上传接口.mp4 2-3 编码实战:“云存储”系统之保存文件元信息.mp4 2-4 编码实战:“云存储‘系统之实现单个文件查询信息接口.mp4 2-5 编码实战:“云存储”系统之实现文件下载接口.mp4 2-6 编码实战:“云存储”系统之实现文件修改接口+小结.mp4 第03章 “云存储”系统之基于MySQL实现的文件数据库 3-1 MySQL基础知识.mp4 3-2 MySQL主从数据同步演示.mp4 3-3 文件表的设计及创建.mp4 3-4 编码实战:“云存储”系统之持久化元数据到文件表.mp4 3-5 编码实战:“云存储”系统之从文件表中获取元数据.mp4 3-6 Docker入门基础文档.mp4 3-6 本章小结.mp4 3-7 Ubuntu中通过Docker安装配置MySQL主从节点.mp4 第04章 “云存储”系统之基于用户系统实现的资源隔离及鉴权 4-1 帐号系统介绍与用户表设计.mp4 4-2 编码实战:“云存储”系统之实现用户注册接口.mp4 4-3 编码实战:“云存储”系统之实现用户登录接口.mp4 4-4 编码实战:“云存储”系统之实现用户信息查询接口.mp4 4-5 接口梳理小结.mp4 4-6 编码实战:“云存储”系统之快速实现访问鉴权接口+小结.mp4 4-7 关于静态资源访问404的问题【补漏.mp4 第05章 “云存储”系统之基于Hash计算实现秒传 5-1 Hash算法对比及秒传原理.mp4 5-2 用户文件表设计与创建.mp4 5-3 编码实战:“云存储”系统之升级改造上传接口.mp4 5-4 编码实战:“云存储”系统之基于用户查询文件Hash信息.mp4 5-5 编码实战:“云存储”系统之实现秒传功能接口+小结.mp4 第06章 “云存储”系统之基于Redis实现分块上传及断点续传 6-1_分块上传与断点续传原理.mp4 6-2_编码实战:Go实现Redis连接池(存储分块信息).mp4 6-3_编码实战:实现初始化分块上传接口.mp4 6-4_编码实战:实现分块上传接口.mp4 6-5_编码实战:实现分块合并接口.mp4 6-6_分块上传场景测试+小结.mp4 6-7_文件断点下载原理.mp4 第07章 “云存储”系统之基于Ceph实现私有云存储服务 7-1_Ceph是什么.mp4 7-2_Ceph集群介绍及兼容亚马逊S3接口详解.mp4 7-3_编码实战:Go访问管理Ceph集群.mp4 7-4_编码实战:Go实现Ceph的文件上传下载+小结.mp4 7-5_Ubuntu下通过Docker快速搭建Ceph测试集群(单机部署).mp4 7-6_Centos7下Docker部署Ceph集群(nautilus最版,多机部署).mp4 第08章 “云存储”系统之基于阿里云OSS实现海量数据上云 8-1_阿里云对象存储OSS简介.mp4 8-2_阿里云对象存储OSS特点.mp4 8-3_阿里云对象存储OSS专业术语.mp4 8-4_阿里云对象存储OSS控制台管理.mp4 8-5_编码实战:OSS上传文件.mp4 8-6_编码实战:OSS下载文件.mp4 8-7_编码实战:OSS对象生命周期管理等常用功能.mp4 8-8_阿里云OSS本章小结.mp4 第09章 “云存储”系统之基于RabbitMQ实现异步存储 9-1_Ubuntu下通过Docker安装RabbitMQ.mp4 9-2_关于任务的同步与异步.mp4 9-3_RabbitMQ简介.mp4 9-4_RabbitMQ工作原理和转发模式.mp4 9-5_Docker安装RabbitMQ及UI管理.mp4 9-6_编码实战_实现异步转移的MQ生产者.mp4 9-7_编码实战_实现异步转移的MQ消费者.mp4 9-8_编码实战_异步转移文件测试+小结.mp4 第10章 “云存储”系统之架构微服务化 10-1_基于Docker部署服务注册发现中心consul集群.mp4 10-2_微服务基础概念与原理.mp4 10-3_云存储系统之微服务架构(1).mp4 10-4_云存储系统之微服务架构(2).mp4 10-5_Web框架Gin基础介绍.mp4 10-6_编码实战_基于Gin改造用户service(1).mp4 10-7_编码实战_基于Gin改造用户service(2.mp4 10-8_gRPC与Protobuf基础原理.mp4 10-9_RPC框架go-micro基础介绍.mp4 10-10_编码实战_改造账号系统service.mp4 10-11_编码实战_改造api网关service.mp4 10-12_编码实战_改造文件上传service.mp4 10-13_综合测试演示+小结.mp4 第11章 “云存储”系统之k8s&Docker;容器化实战 11-1_Ubuntu18下通过kubeadm单机安装k8s(v1.14.1)集群.mp4 11-2_Ubuntu18下安装k8s(v1.14.1)可视化管理工具.mp4 11-3_Docker与Docker-Compose基础概念.mp4 11-4_基于容器的微服务反向代理利器Traefik.mp4 11-5_基于Docker-compose与Traefik的容器化部署演示.mp4 11-6_Kubernetes基础原理.mp4 11-7_基于Kubernetes的容器化部署演示.mp4 第12章 “云存储”系统之持续集成部署 12-1_ubuntu下离线安装harbor1.6.mp4 12-2_持续构建之基础概念.mp4 12-3_基于gitlab+jenkins+harbor的自动化部署配置演示.mp4 第13章 课程总结 13-1_课程总结之章节重点及技能树温习.mp4
官方下载到的go module文档 Table of Contents The "Quick Start" and "New Concepts" sections are particularly important for someone who is starting to work with modules. The "How to..." sections cover more details on mechanics. The largest quantity of content on this page is in the FAQs answering more specific questions; it can be worthwhile to at least skim the FAQ one-liners listed here. Quick Start Example Daily Workflow New Concepts Modules go.mod Version Selection Semantic Import Versioning How to Use Modules How to Install and Activate Module Support How to Define a Module How to Upgrade and Downgrade Dependencies How to Prepare for a Release (All Versions) How to Prepare for a Release (v2 or Higher) Publishing a Release Migrating to Modules Additional Resources Changes Since the Initial Vgo Proposal GitHub Issues FAQs How are versions marked as incompatible? When do I get old behavior vs. new module-based behavior? Why does installing a tool via 'go get' fail with error 'cannot find main module'? How can I track tool dependencies for a module? What is the status of module support in IDEs, editors and standard tools like goimports, gorename, etc.? FAQs — Additional Control What community tooling exists for working with modules? When should I use the 'replace' directive? Can I work entirely outside of VCS on my local filesystem? How do I use vendoring with modules? Is vendoring going away? Are there "always on" module repositories and enterprise proxies? Can I control when go.mod gets updated and when the go tools use the network to satisfy dependencies? How do I use modules with CI systems such as Travis or CircleCI? How do I download modules needed to build specific packages or tests? FAQs — go.mod and go.sum Why does 'go mod tidy' record indirect and test dependencies in my 'go.mod'? Is 'go.sum' a lock file? Why does 'go.sum' include information for module versions I am no longer using? Should I still add a 'go.mod' file if I do not have any dependencies? Should I commit my 'go.sum' file as well as my 'go.mod' file? FAQs — Semantic Import Versioning Why must major version numbers appear in import paths? Why are major versions v0, v1 omitted from import paths? What are some implications of tagging my project with major version v0, v1, or making breaking changes with v2+? Can a module consume a package that has not opted in to modules? Can a module consume a v2+ package that has not opted into modules? What does '+incompatible' mean? How are v2+ modules treated in a build if modules support is not enabled? How does "minimal module compatibility" work in 1.9.7+, 1.10.3+, and 1.11? What happens if I create a go.mod but do not apply semver tags to my repository? Can a module depend on a different version of itself? FAQs — Multi-Module Repositories What are multi-module repositories? Should I have multiple modules in a single repository? Is it possible to add a module to a multi-module repository? Is it possible to remove a module from a multi-module repository? Can a module depend on an internal/ in another? Can an additional go.mod exclude unnecessary content? Do modules have the equivalent of a .gitignore file? FAQs — Minimal Version Selection Won't minimal version selection keep developers from getting important updates? FAQs — Possible Problems What are some general things I can spot check if I am seeing a problem? What can I check if I am not seeing the expected version of a dependency? Why am I getting an error 'cannot find module providing package foo'? Why does 'go mod init' give the error 'cannot determine module path for source directory'? I have a problem with a complex dependency that has not opted in to modules. Can I use information from its current dependency manager? How can I resolve "parsing go.mod: unexpected module path" and "error loading module requirements" errors caused by a mismatch between import paths vs. declared module identity? Why does 'go build' require gcc, and why are prebuilt packages such as net/http not used? Do modules work with relative imports like import "./subdir"? Some needed files may not be present in populated vendor directory

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论 5

打赏作者

笨笨小弟

求施舍,只求生存。

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值