1 Star 0 Fork 0

wait4me/selfGoLib

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
file.go 10.34 KB
一键复制 编辑 原始数据 按行查看 历史
wait4me 提交于 2026-02-11 14:06 +08:00 . feat: initial release of selfGoLib toolkit
package fileUtil
import (
"bufio"
"crypto/hmac"
"encoding/hex"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/tjfoc/gmsm/sm3"
)
const PathSeparatorStr = string(os.PathSeparator)
// Mov 移动文件或目录。如果目标路径已存在,会覆盖目标文件或目录。
// 参数:
// - srcFile: 源文件或目录路径。
// - dstFile: 目标文件或目录路径。
//
// 返回值:
// - error: 错误信息。
func Mov(srcFile, dstFile string) error {
if err := Copy(srcFile, dstFile); err != nil {
return err
}
f, err := os.Lstat(srcFile)
if err != nil {
return err
}
if f.IsDir() {
return os.RemoveAll(srcFile)
}
return os.Remove(srcFile)
}
// Copy 复制文件或目录。如果目标路径不存在,会自动创建对应目录。
// 参数:
// - srcFile: 源文件或目录路径。
// - dstFile: 目标文件或目录路径。
//
// 返回值:
// - error: 错误信息。
func Copy(srcFile, dstFile string) error {
srcInfo, err := os.Lstat(srcFile)
if err != nil {
return nil
}
if srcInfo.IsDir() {
//源路径是文件夹
return copyDirToUnknown(srcFile, dstFile)
}
return copyFileToUnknown(srcFile, dstFile)
}
// statDst 检查目标路径的状态。
// 如果目标路径存在,则返回对应的 fs.FileInfo;
// 如果目标路径不存在且以路径分隔符结尾,则创建目录并返回其 fs.FileInfo;
// 如果目标路径不存在且不以路径分隔符结尾,则检查其父目录是否存在,若不存在则创建,并返回 nil。
// 参数:
// - dst: 目标路径。
//
// 返回值:
// - fs.FileInfo: 目标路径的文件信息(如果存在)。
// - error: 错误信息。
func statDst(dst string) (fs.FileInfo, error) {
dstInfo, err := os.Lstat(dst)
if err == nil {
return dstInfo, err
}
if !os.IsNotExist(err) {
//报错,但错误不是文件不存在的错误
return nil, err
}
if strings.HasSuffix(dst, PathSeparatorStr) {
//以"/"结尾则认为是目录,新建目录并将源文件拷贝到该目录下
err = os.MkdirAll(dst, os.ModePerm)
if err != nil {
return nil, err
}
dstInfo, _ = os.Stat(dst)
return dstInfo, nil
}
baseDir := filepath.Dir(dst)
is, err := PathExists(baseDir)
if err != nil {
return nil, err
}
if !is {
err = os.MkdirAll(baseDir, os.ModePerm)
if err != nil {
return nil, err
}
}
return nil, nil
}
// copyFileToUnknown 将源文件复制到目标路径。
// 该函数要求源路径必须指向一个已存在的文件。
// 参数:
// - src: 源文件路径。
// - dst: 目标路径。
//
// 返回值:
// - error: 错误信息。
func copyFileToUnknown(src, dst string) error {
dstInfo, err := statDst(dst)
if err != nil {
return err
}
if dstInfo == nil {
//目标路径不存在
return copyFileToFile(src, dst)
}
if dstInfo.IsDir() {
//目标路径存在且是一个目录时,将源文件拷贝至该目录
srcInfo, _ := os.Lstat(src)
return copyFileToFile(src, filepath.Join(dst, srcInfo.Name()))
}
//目标路径存在且不是目录,拷贝源文件内容覆盖目标玩家
return copyFileToFile(src, dst)
}
// copyDirToUnknown 将源目录复制到目标路径。
// 该函数要求源路径必须指向一个已存在的目录。
// 参数:
// - src: 源目录路径。
// - dst: 目标路径。
//
// 返回值:
// - error: 错误信息。
func copyDirToUnknown(src, dst string) error {
dstInfo, err := statDst(dst)
if err != nil {
return err
}
if dstInfo == nil {
dstInfo, _ = statDst(dst + PathSeparatorStr)
}
if dstInfo.IsDir() {
//目标路径也是目录,拷贝源路径中所有内容至目标路径
filepath.WalkDir(src, walkAndCopy(src, dst))
return nil
}
panic("不能将一个目录拷贝为一个文件")
}
// walkAndCopy 遍历源目录并将其内容复制到目标目录。
// 参数:
// - src: 源目录路径。
// - dst: 目标目录路径。
//
// 返回值:
// - fs.WalkDirFunc: 用于遍历和复制的函数。
func walkAndCopy(src, dst string) fs.WalkDirFunc {
return func(path string, d fs.DirEntry, err error) error {
relativePath, _ := filepath.Rel(src, path)
newDstPath := filepath.Join(dst, relativePath)
if d.IsDir() {
_, err1 := statDst(newDstPath + PathSeparatorStr)
return err1
}
return copyFileToFile(path, newDstPath)
}
}
// copyFileToFile 将源文件内容复制到目标文件。
// 参数:
// - src: 源文件路径。
// - dst: 目标文件路径。
//
// 返回值:
// - error: 错误信息。
func copyFileToFile(src, dst string) error {
// Read all content of src to data
data, err := os.ReadFile(src)
if err != nil {
return err
}
info, _ := os.Stat(src)
// Write data to dst
// return os.WriteFile(dst, data, os.ModePerm)
return os.WriteFile(dst, data, info.Mode())
}
// PathExists 检查指定路径是否存在。
// 参数:
// - path: 需要检查的文件或目录路径。
//
// 返回值:
// - bool: 如果路径存在,返回 true;否则返回 false。
// - error: 如果检查过程中发生错误(非“路径不存在”错误),返回该错误;否则返回 nil。
//
// 注意:
// - 如果路径不存在,不会返回错误,而是返回 false 和 nil。
// - 其他错误(如权限不足)会直接返回。
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
//os.IsNotExist来判断,是不是不存在的错误
if os.IsNotExist(err) { //如果返回的错误类型使用os.isNotExist()判断为true,说明文件或者文件夹不存在
return false, nil
}
return false, err //如果有错误了,但是不是不存在的错误,所以把这个错误原封不动的返回
}
// ReadFileLines 读取文件内容并按指定分隔符分割为多行字符串。
// 参数:
// - path: 文件路径。
// - delim: 行分隔符。
//
// 返回值:
// - []string: 分割后的字符串数组。
func ReadFileLines(path string, delim byte) []string {
return ReadFileLinesTrimDelim(path, delim, false)
}
func ReadFileLinesTrimDelim(path string, delim byte, isTrim bool) []string {
var lines []string
if _, err := os.Lstat(path); err != nil {
return lines
}
f, _ := os.Open(path)
defer f.Close()
r := bufio.NewReader(f)
for {
line, err := r.ReadString(delim)
if err != nil && err != io.EOF {
panic(err)
}
if err == io.EOF {
//ReadString在返回EOF时包含数据
line = strings.TrimSpace(line)
if line != "" {
lines = append(lines, line)
}
break
}
line = strings.TrimSuffix(strings.TrimSpace(line), string(delim))
lines = append(lines, line)
}
return lines
}
// GetSystemRootDir 返回操作系统安装位置的根目录
// Windows: C:\ (基于 SystemDrive/SystemRoot 环境变量)
// Unix: /
func GetSystemRootDir() string {
if runtime.GOOS == "windows" {
// 优先使用 SystemDrive (如 "C:")
if sd := os.Getenv("SystemDrive"); sd != "" {
return filepath.Clean(sd + string(os.PathSeparator))
}
// 备用:从 SystemRoot (如 "C:\\Windows") 提取卷名
if sr := os.Getenv("SystemRoot"); sr != "" {
if vol := filepath.VolumeName(sr); vol != "" {
return filepath.Clean(vol + string(os.PathSeparator))
}
}
return `C:\` // 安全兜底
}
return "/" // 所有 Unix-like 系统(含 WSL)
}
// GetExecDir 返回当前执行文件所在的绝对路径(已解析符号链接)
func GetExecDir() string {
// 获取当前执行文件绝对路径
exePath, _ := os.Executable()
exePath, _ = filepath.EvalSymlinks(filepath.Dir(exePath))
return exePath
}
// ComputeFileHMAC 计算指定文件的HMAC-SM3值
//
// 参数:
// - filePath: 要计算的文件路径
// - key: 用于HMAC计算的密钥
//
// 返回值:
// - string: 计算得到的HMAC十六进制字符串
// - error: 操作过程中遇到的错误
func ComputeFileHMAC(filePath string, key []byte) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("打开文件失败: %w", err)
}
defer file.Close()
h := hmac.New(sm3.New, key)
if _, err := io.Copy(h, file); err != nil {
return "", fmt.Errorf("读取文件失败: %w", err)
}
return hex.EncodeToString(h.Sum(nil)), nil
}
// ComputeFileHMACAndWrite 计算指定文件的HMAC值并写入.hmac文件
//
// 参数:
//
// filePath: 要计算HMAC的文件路径
// key: 用于HMAC计算的密钥
//
// 返回:
//
// error: 操作过程中发生的错误,包括HMAC计算失败或文件写入失败
func ComputeFileHMACAndWrite(filePath string, key []byte) error {
hmacHex, err := ComputeFileHMAC(filePath, key)
if err != nil {
return fmt.Errorf("计算HMAC失败: %w", err)
}
// 计算 HMAC-SM3
hmacFile := filePath + ".hmac"
// 写入内容格式: "hmac-sm3:<十六进制值>\n"(兼容 openssl 等工具惯例)
if err := os.WriteFile(hmacFile, fmt.Appendf(nil, "hmac-sm3:%s", hmacHex), 0644); err != nil {
return fmt.Errorf("写入HMAC文件失败: %w", err)
}
return nil
}
// ValidateHMAC 验证指定文件的HMAC签名是否匹配
// 参数:
// - filePath: 要验证的文件路径
// - key: 用于HMAC计算的密钥
//
// 返回:
// - error: 如果验证失败或发生错误则返回错误信息,验证成功返回nil
//
// 注意:
// - 会查找同目录下以.hmac为后缀的HMAC签名文件进行比对
func ValidateHMAC(filePath string, key []byte) error {
hmacFile := filePath + ".hmac"
if _, err := os.Stat(hmacFile); err == nil {
expectedHmac, err := os.ReadFile(hmacFile)
if err != nil {
return fmt.Errorf("读取预期HMAC文件失败: %w", err)
}
hmacHex, err := ComputeFileHMAC(filePath, key)
if err != nil {
return fmt.Errorf("计算HMAC失败: %w", err)
}
content := fmt.Sprintf("hmac-sm3:%s", hmacHex)
if content != string(expectedHmac) {
return fmt.Errorf("HMAC校验失败: 计算结果[%s]与预期不一致", content)
}
}
return nil
}
// MandatoryValidateHMAC 校验文件及其HMAC签名文件是否存在并验证签名
//
// 参数:
//
// filePath - 需要校验的文件路径
// key - 用于HMAC校验的密钥
//
// 返回:
//
// error - 如果HMAC文件不存在或校验失败则返回错误
//
// 注意:
// - 会查找同目录下以.hmac为后缀的HMAC签名文件进行比对
func MandatoryValidateHMAC(filePath string, key []byte) error {
hmacFile := filePath + ".hmac"
if _, err := os.Stat(hmacFile); err != nil {
return fmt.Errorf("缺少HMAC文件: %s", hmacFile)
}
return ValidateHMAC(filePath, key)
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/wait4me/selfGoLib.git
git@gitee.com:wait4me/selfGoLib.git
wait4me
selfGoLib
selfGoLib
v1.0.2

搜索帮助