Skip to content

Commit

Permalink
判断本地与远端是否一致的方法支持符号链接策略和空文件夹
Browse files Browse the repository at this point in the history
- IsSame方法改进,增加对符号链接策略的匹配支持,增加对空文件夹的支持
  • Loading branch information
jorben committed Mar 27, 2024
1 parent 5d4b510 commit 65cbaf2
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 10 deletions.
6 changes: 2 additions & 4 deletions checkjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,9 @@ func (c *CheckJob) Walk(ctx context.Context) {
if d.IsDir() && helper.IsIgnore(path, c.Ignore) {
return filepath.SkipDir
}
// todo 空文件夹处理,非空文件夹只需处理其内部文件即可
// todo 符号链接处理
// 对比文件
if !d.IsDir() && !helper.IsIgnore(path, c.Ignore) {
if isSame := c.Storage.IsSame(ctx, path, ""); !isSame {
if !helper.IsIgnore(path, c.Ignore) {
if isSame := c.Storage.IsSameV2(ctx, path, ""); !isSame {
// 文件存在差异,丢入变更队列
c.PutChan <- path
log.Infof("Differences found %s", path)
Expand Down
107 changes: 101 additions & 6 deletions storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ func (s *Storage) RemoveObjects(ctx context.Context, objectPath string) (someErr

// FPutObject 上传对象
func (s *Storage) FPutObject(ctx context.Context, localPath string) error {
// 文件 则需要对远端内容一致性比较,内容一致则不重复上传
if isSame := s.IsSameV2(ctx, localPath, ""); isSame {
return enum.ErrSkipTransfer
}
objectName := localPath
// 判断是否符号链接
if isLink, _ := helper.IsSymlink(localPath); isLink {
Expand Down Expand Up @@ -175,10 +179,6 @@ func (s *Storage) FPutObject(ctx context.Context, localPath string) error {
}

objectName = s.GetRemotePath(objectName)
// 文件 则需要对远端内容一致性比较,内容一致则不重复上传
if isSame := s.IsSame(ctx, localPath, objectName); isSame {
return enum.ErrSkipTransfer
}

tmp := localPath
// 先拷贝 再上传
Expand All @@ -204,8 +204,101 @@ func (s *Storage) FPutObject(ctx context.Context, localPath string) error {
return nil
}

// IsSameV2 判断本地文件和远端文件内容是否一致,相较于V1新增包含了符号链接、空文件夹的判断
func (s *Storage) IsSameV2(ctx context.Context, localPath, remotePath string) bool {
var err error
var localMd5 string
if remotePath == "" {
remotePath = s.GetRemotePath(localPath)
}
isLink, _ := helper.IsSymlink(localPath)
// 判断本地路径是否符号链接
if isLink {
switch s.SymLink {
case enum.SymlinkSkip:
log.Debugf("SymlinkSkip %s", localPath)
return true
case enum.SymlinkFile:
if isDir, _ := helper.IsDir(localPath); !isDir {
log.Debugf("SymlinkFile %s", localPath)
localMd5, err = helper.Md5(localPath)
if err != nil {
log.Errorf("MD5 error: %s", err.Error())
return false
}
break
}
// 如果是文件夹 则应用Addr策略
log.Debugf("Dir fallthrough to SymlinkAddr %s", localPath)
fallthrough
case enum.SymlinkAddr:
log.Debugf("SymlinkAddr %s", localPath)
remotePath += ".link"
// 获取目标地址
target, _ := helper.GetSymlinkTarget(localPath)
// 计算md5值
localMd5, err = helper.Md5(target)
if err != nil {
log.Errorf("MD5 error: %s", err.Error())
return false
}
default:
return true
}
}

// 非符号链接的目录
if isDir, _ := helper.IsDir(localPath); !isLink && isDir {
// 判断是否非空,非空直接过
if isEmpty, _ := helper.IsDirEmpty(localPath); !isEmpty {
log.Debugf("Skip dir, is not empty %s", localPath)
return true
} else {
// 空目录用.keep文件构建
remotePath += "/.keep"
localMd5 = "d41d8cd98f00b204e9800998ecf8427e"
}
}

objectInfo, err := s.Minio.StatObject(ctx, s.Bucket, remotePath, minio.StatObjectOptions{})
if err != nil {
// 多半是Key不存在
log.Debugf("StatObject %s, path: %s", err.Error(), remotePath)
return false
}
// 是否分片上传的文件,分片上传的Etag是各分片MD5值合并后的MD5,所以与文件MCD5不一致,且ETAG带有分片数量标识
// 分片场景,通过校验文件大小和修改时间来判断是否一致
if strings.Contains(objectInfo.ETag, "-") {
fileInfo, err := os.Stat(localPath)
if err != nil {
log.Errorf("Stat file err: %s", err.Error())
return false
}
log.Debugf("Compare big file: %s, Size: %d, ModifyTime:%s, Remote Size:%d, ModifyTime:%s",
localPath, fileInfo.Size(), fileInfo.ModTime().Format("2006-01-02 15:04:05"),
objectInfo.Size, objectInfo.LastModified.In(time.Now().Location()).Format("2006-01-02 15:04:05"))
if fileInfo.Size() != objectInfo.Size || fileInfo.ModTime().After(objectInfo.LastModified) {
return false
}
return true
}

// 计算本地文件的md5
if localMd5 == "" {
localMd5, _ = helper.Md5(localPath)
}
log.Debugf("Compare %s, Local Md5: %s, Remote ETag: %s", localPath, localMd5, objectInfo.ETag)
if strings.ToLower(localMd5) == strings.ToLower(objectInfo.ETag) {
return true
}
return false

}

// IsSame 判断本地文件和远端文件内容是否一致
func (s *Storage) IsSame(ctx context.Context, localPath, remotePath string) bool {
//
// Deprecated: 已升级IsSameV2
func (s *Storage) IsSame(ctx context.Context, localPath, localMd5, remotePath string) bool {
if remotePath == "" {
remotePath = s.GetRemotePath(localPath)
}
Expand Down Expand Up @@ -233,7 +326,9 @@ func (s *Storage) IsSame(ctx context.Context, localPath, remotePath string) bool
}

// 计算本地文件的md5
localMd5, _ := helper.Md5(localPath)
if localMd5 == "" {
localMd5, _ = helper.Md5(localPath)
}
log.Debugf("Compare %s, Local Md5: %s, Remote ETag: %s", remotePath, localMd5, objectInfo.ETag)
if strings.ToLower(localMd5) == strings.ToLower(objectInfo.ETag) {
return true
Expand Down

0 comments on commit 65cbaf2

Please sign in to comment.