154 lines
2.5 KiB
Go
154 lines
2.5 KiB
Go
package files
|
||
|
||
import (
|
||
"errors"
|
||
"io"
|
||
"lab.evlic.cn/go/pkg/md5"
|
||
"mime/multipart"
|
||
"os"
|
||
|
||
"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
|
||
}
|