2023-09-18 15:58:52 +08:00
|
|
|
|
package files
|
2023-09-18 15:51:20 +08:00
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"io"
|
2023-09-18 15:58:52 +08:00
|
|
|
|
"lab.evlic.cn/go/pkg/md5"
|
2023-09-18 15:51:20 +08:00
|
|
|
|
"mime/multipart"
|
2023-09-18 15:58:52 +08:00
|
|
|
|
"os"
|
2023-09-18 15:51:20 +08:00
|
|
|
|
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func FileSave(r io.Reader, dst string) (err error) {
|
|
|
|
|
var (
|
|
|
|
|
fp string
|
|
|
|
|
out io.WriteCloser
|
|
|
|
|
)
|
|
|
|
|
fp = filepath.Dir(dst)
|
|
|
|
|
err = DirCheck(fp)
|
|
|
|
|
|
|
|
|
|
out, err = os.Create(dst)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer out.Close()
|
|
|
|
|
|
|
|
|
|
_, err = io.Copy(out, r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FileMD5 计算文件 md5 值
|
|
|
|
|
func FileMD5(file string) (string, error) {
|
|
|
|
|
f, err := os.Open(file)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info, err := f.Stat()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size := info.Size()
|
|
|
|
|
return md5.Sum(f, size, BlockSize, ChunkSize)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MultipartMD5(fh *multipart.FileHeader) (string, error) {
|
|
|
|
|
f, err := fh.Open()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size := fh.Size
|
|
|
|
|
return md5.Sum(f, size, BlockSize, ChunkSize)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
BlockSize = 1 << 10
|
|
|
|
|
ChunkSize = BlockSize << 2
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
ErrorIsNotDir = errors.New("path is not dir. ")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func DirCheck(dir string) (err error) {
|
|
|
|
|
var fi os.FileInfo
|
|
|
|
|
if fi, err = os.Stat(dir); err != nil {
|
|
|
|
|
if !os.IsExist(err) || os.IsNotExist(err) {
|
|
|
|
|
return DirMk(dir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !fi.IsDir() {
|
|
|
|
|
err = ErrorIsNotDir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func DirMk(dir string) error {
|
|
|
|
|
return os.MkdirAll(dir, 0755)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MaxScanDeep = 20
|
|
|
|
|
|
|
|
|
|
var maxDeepError = errors.New("scanMaxDeep")
|
|
|
|
|
|
|
|
|
|
func IsScanMaxDeep(err error) bool {
|
|
|
|
|
return errors.Is(err, maxDeepError)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DirScan 扫描目录,on err skip
|
|
|
|
|
func DirScan(dir string, h func(dir string, info os.FileInfo)) error {
|
|
|
|
|
return dirScan(dir, h, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dirScanIgnore(name string) bool {
|
|
|
|
|
return strings.HasPrefix(name, ".") ||
|
|
|
|
|
strings.Contains(strings.ToLower(name), "readme")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dirScan(dir string, h func(dir string, info os.FileInfo), deep int) error {
|
|
|
|
|
if deep > MaxScanDeep {
|
|
|
|
|
return maxDeepError
|
|
|
|
|
}
|
|
|
|
|
dirs, err := os.ReadDir(dir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
info os.FileInfo
|
|
|
|
|
nextDir string
|
|
|
|
|
)
|
|
|
|
|
for _, entry := range dirs {
|
|
|
|
|
info, err = entry.Info()
|
|
|
|
|
if err != nil {
|
|
|
|
|
zap.L().Warn("scan err", zap.String("name", entry.Name()), zap.Error(err))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if dirScanIgnore(info.Name()) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h(dir, info)
|
|
|
|
|
if entry.IsDir() {
|
|
|
|
|
nextDir = filepath.Join(dir, info.Name())
|
|
|
|
|
if err = dirScan(nextDir, h, deep+1); err != nil {
|
|
|
|
|
zap.L().Warn("scan err", zap.String("dirname", nextDir), zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func PathAbs(path string) string {
|
|
|
|
|
if filepath.IsAbs(path) {
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
abs, err := filepath.Abs(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
return abs
|
|
|
|
|
}
|