OperateFile

本节介绍 Golang 中常用的文件与网络操作封装:CSV 读写、环境变量文件生成、带会话保持的 HTTP 客户端。这些是链上数据落盘、CLI 工具开发中的高频操作。

ReadCSV

读取 CSV 文件的标准流程:os.Open 打开文件 → csv.NewReader 构造读取器 → ReadAll 一次性读入全部行。返回的 lines 是二维切片 [][]string,每个元素是一行的字段列表。

reader.Comma 指定分隔符,默认为 ,,也可以改为 \t 读取 TSV 文件:

file, err := os.Open("file.csv")
if err != nil {
	log.Fatal(err)
}
defer file.Close()

reader := csv.NewReader(file)
reader.Comma = ','

lines, err := reader.ReadAll()
if err != nil {
	log.Fatal(err)
}

for index, line := range lines {
	fmt.Println(index, line) // line 为 []string,对应一行中的各个字段
}

WriteCSV

写入 CSV 的流程:os.OpenFile 以追加模式打开(os.O_CREATE|os.O_WRONLY|os.O_APPEND)→ csv.NewWriter 构造写入器 → 循环 Write 写入行 → defer writer.Flush() 刷盘。

注意 Flush 必须调用,否则数据可能还停留在缓冲区没有落盘。下面的示例将链上交易记录批量导出为 CSV 文件:

func ParseTxToCSV(res *[]gettxbyaddress.Result, apiKey string) {
	file, err := os.OpenFile("file.csv", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			log.Fatal(err)
		}
	}(file)

	writer := csv.NewWriter(file)
	defer writer.Flush()

	for _, txRes := range *res {
		ERC20 := checkERC20(txRes.To, apiKey)
		err := writer.Write([]string{
			txRes.BlockNumber,
			txRes.TimeStamp,
			txRes.Hash,
			txRes.Nonce,
			txRes.BlockHash,
			txRes.TransactionIndex,
			txRes.From,
			txRes.To,
			ERC20,
			txRes.Value,
			txRes.Gas,
			txRes.GasPrice,
			txRes.IsError,
			txRes.TxReceiptStatus,
			txRes.Input,
			txRes.ContractAddress,
			txRes.CumulativeGasUsed,
			txRes.GasUsed,
			txRes.Confirmations,
			txRes.MethodId,
			txRes.FunctionName,
		})
		if err != nil {
			log.Fatal(err)
		}
	}
}

WriteEnvFile

生成 .env 风格的配置文件。要点:

  • 用 strings.Join(lines, "\n") 拼接内容,保证末尾换行
  • os.WriteFile 的第三个参数是文件权限,0600 表示仅文件所有者可读写——密钥类配置必须收紧权限,避免被同机其他用户读取
  • 注释中提醒将文件加入 .gitignore,防止敏感信息被提交到仓库
func onboardWriteEnvFile(path, postgresDSN, gatewayToken, encryptionKey string) {
	var lines []string
	lines = append(lines, "# GoClaw — auto-generated by onboard")
	lines = append(lines, "# Keep this file secret! Add to .gitignore.")
	lines = append(lines, "")
	lines = append(lines, fmt.Sprintf("export GOCLAW_POSTGRES_DSN=%s", postgresDSN))
	lines = append(lines, fmt.Sprintf("export GOCLAW_GATEWAY_TOKEN=%s", gatewayToken))
	lines = append(lines, fmt.Sprintf("export GOCLAW_ENCRYPTION_KEY=%s", encryptionKey))
	lines = append(lines, "")

	content := strings.Join(lines, "\n")
	_ = os.WriteFile(path, []byte(content), 0600)
}

HttpWithSameSession

默认的 http.Client 每次请求相互独立、不保留 Cookie。要模拟「同一会话」(例如登录后保持会话连续请求),需要给 http.Client 挂上 cookiejar,由它负责在各次请求间自动存取 Cookie:

  • cookiejar.New(nil) 创建会话存储,Cookie 会按域名自动管理
  • DisableKeepAlives: true 关闭连接复用,每次请求使用新连接,适合配合代理或避免服务端连接限制
  • MaxIdleConnsPerHost: -1 表示不保留每主机空闲连接
  • Timeout: 0 表示不设置整体超时(如需超时控制可设为具体时长)
func createHttpClient() (*http.Client, error) {
	jar, err := cookiejar.New(nil)
	if err != nil {
		return nil, fmt.Errorf("cookiejar.New error %s", err.Error())
	}
	return &http.Client{
		Jar: jar,
		Transport: &http.Transport{
			DisableKeepAlives:   true,
			MaxIdleConnsPerHost: -1,
		},
		Timeout: 0,
	}, nil
}