github.com/iikira/iikira-go-utils@v0.0.0-20230610031953-f2cb11cde33a/utils/checksum/checksum.go (about)

     1  // Package checksum 校验本地文件包
     2  package checksum
     3  
     4  import (
     5  	"crypto/md5"
     6  	"github.com/iikira/iikira-go-utils/utils/cachepool"
     7  	"github.com/iikira/iikira-go-utils/utils/converter"
     8  	"hash/crc32"
     9  	"io"
    10  	"os"
    11  )
    12  
    13  const (
    14  	// DefaultBufSize 默认的bufSize
    15  	DefaultBufSize = int(256 * converter.KB)
    16  )
    17  
    18  const (
    19  	// CHECKSUM_MD5 获取文件的 md5 值
    20  	CHECKSUM_MD5 int = 1 << iota
    21  	// CHECKSUM_SLICE_MD5 获取文件前 sliceSize 切片的 md5 值
    22  	CHECKSUM_SLICE_MD5
    23  	// CHECKSUM_CRC32 获取文件的 crc32 值
    24  	CHECKSUM_CRC32
    25  )
    26  
    27  type (
    28  	// LocalFileMeta 本地文件元信息
    29  	LocalFileMeta struct {
    30  		Path     string `json:"path"`     // 本地路径
    31  		Length   int64  `json:"length"`   // 文件大小
    32  		SliceMD5 []byte `json:"slicemd5"` // 文件前 requiredSliceLen (256KB) 切片的 md5 值
    33  		MD5      []byte `json:"md5"`      // 文件的 md5
    34  		CRC32    uint32 `json:"crc32"`    // 文件的 crc32
    35  		ModTime  int64  `json:"modtime"`  // 修改日期
    36  	}
    37  
    38  	// LocalFileChecksum 校验本地文件
    39  	LocalFileChecksum struct {
    40  		LocalFileMeta
    41  		bufSize   int
    42  		sliceSize int
    43  		buf       []byte
    44  		file      *os.File // 文件
    45  	}
    46  )
    47  
    48  func NewLocalFileChecksum(localPath string, sliceSize int) *LocalFileChecksum {
    49  	return NewLocalFileChecksumWithBufSize(localPath, DefaultBufSize, sliceSize)
    50  }
    51  
    52  func NewLocalFileChecksumWithBufSize(localPath string, bufSize, sliceSize int) *LocalFileChecksum {
    53  	return &LocalFileChecksum{
    54  		LocalFileMeta: LocalFileMeta{
    55  			Path: localPath,
    56  		},
    57  		bufSize:   bufSize,
    58  		sliceSize: sliceSize,
    59  	}
    60  }
    61  
    62  // OpenPath 检查文件状态并获取文件的大小 (Length)
    63  func (lfc *LocalFileChecksum) OpenPath() error {
    64  	if lfc.file != nil {
    65  		lfc.file.Close()
    66  	}
    67  
    68  	var err error
    69  	lfc.file, err = os.Open(lfc.Path)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	info, err := lfc.file.Stat()
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	lfc.Length = info.Size()
    80  	lfc.ModTime = info.ModTime().Unix()
    81  	return nil
    82  }
    83  
    84  // GetFile 获取文件
    85  func (lfc *LocalFileChecksum) GetFile() *os.File {
    86  	return lfc.file
    87  }
    88  
    89  // Close 关闭文件
    90  func (lfc *LocalFileChecksum) Close() error {
    91  	if lfc.file == nil {
    92  		return ErrFileIsNil
    93  	}
    94  
    95  	return lfc.file.Close()
    96  }
    97  
    98  func (lfc *LocalFileChecksum) initBuf() {
    99  	if lfc.buf == nil {
   100  		lfc.buf = cachepool.RawMallocByteSlice(lfc.bufSize)
   101  	}
   102  }
   103  
   104  func (lfc *LocalFileChecksum) writeChecksum(data []byte, wus ...*ChecksumWriteUnit) (err error) {
   105  	doneCount := 0
   106  	for _, wu := range wus {
   107  		_, err := wu.Write(data)
   108  		switch err {
   109  		case ErrChecksumWriteStop:
   110  			doneCount++
   111  			continue
   112  		case nil:
   113  		default:
   114  			return err
   115  		}
   116  	}
   117  	if doneCount == len(wus) {
   118  		return ErrChecksumWriteAllStop
   119  	}
   120  	return nil
   121  }
   122  
   123  func (lfc *LocalFileChecksum) repeatRead(wus ...*ChecksumWriteUnit) (err error) {
   124  	if lfc.file == nil {
   125  		return ErrFileIsNil
   126  	}
   127  
   128  	lfc.initBuf()
   129  
   130  	defer func() {
   131  		_, err = lfc.file.Seek(0, os.SEEK_SET) // 恢复文件指针
   132  		if err != nil {
   133  			return
   134  		}
   135  	}()
   136  
   137  	// 读文件
   138  	var (
   139  		n int
   140  	)
   141  read:
   142  	for {
   143  		n, err = lfc.file.Read(lfc.buf)
   144  		switch err {
   145  		case io.EOF:
   146  			err = lfc.writeChecksum(lfc.buf[:n], wus...)
   147  			break read
   148  		case nil:
   149  			err = lfc.writeChecksum(lfc.buf[:n], wus...)
   150  		default:
   151  			return
   152  		}
   153  	}
   154  	switch err {
   155  	case ErrChecksumWriteAllStop: // 全部结束
   156  		err = nil
   157  	}
   158  	return
   159  }
   160  
   161  func (lfc *LocalFileChecksum) createChecksumWriteUnit(cw ChecksumWriter, isAll, isSlice bool, getSumFunc func(sliceSum interface{}, sum interface{})) (wu *ChecksumWriteUnit, deferFunc func(err error)) {
   162  	wu = &ChecksumWriteUnit{
   163  		ChecksumWriter: cw,
   164  		End:            lfc.LocalFileMeta.Length,
   165  		OnlySliceSum:   !isAll,
   166  	}
   167  
   168  	if isSlice {
   169  		wu.SliceEnd = int64(lfc.sliceSize)
   170  	}
   171  
   172  	return wu, func(err error) {
   173  		if err != nil {
   174  			return
   175  		}
   176  		getSumFunc(wu.SliceSum, wu.Sum)
   177  	}
   178  }
   179  
   180  // Sum 计算文件摘要值
   181  func (lfc *LocalFileChecksum) Sum(checkSumFlag int) (err error) {
   182  	lfc.fix()
   183  	wus := make([]*ChecksumWriteUnit, 0, 2)
   184  	if (checkSumFlag & (CHECKSUM_MD5 | CHECKSUM_SLICE_MD5)) != 0 {
   185  		md5w := md5.New()
   186  		wu, d := lfc.createChecksumWriteUnit(
   187  			NewHashChecksumWriter(md5w),
   188  			(checkSumFlag&CHECKSUM_MD5) != 0,
   189  			(checkSumFlag&CHECKSUM_SLICE_MD5) != 0,
   190  			func(sliceSum interface{}, sum interface{}) {
   191  				if sliceSum != nil {
   192  					lfc.SliceMD5 = sliceSum.([]byte)
   193  				}
   194  				if sum != nil {
   195  					lfc.MD5 = sum.([]byte)
   196  				}
   197  			},
   198  		)
   199  
   200  		wus = append(wus, wu)
   201  		defer d(err)
   202  	}
   203  	if (checkSumFlag & CHECKSUM_CRC32) != 0 {
   204  		crc32w := crc32.NewIEEE()
   205  		wu, d := lfc.createChecksumWriteUnit(
   206  			NewHash32ChecksumWriter(crc32w),
   207  			true,
   208  			false,
   209  			func(sliceSum interface{}, sum interface{}) {
   210  				if sum != nil {
   211  					lfc.CRC32 = sum.(uint32)
   212  				}
   213  			},
   214  		)
   215  
   216  		wus = append(wus, wu)
   217  		defer d(err)
   218  	}
   219  
   220  	err = lfc.repeatRead(wus...)
   221  	return
   222  }
   223  
   224  func (lfc *LocalFileChecksum) fix() {
   225  	if lfc.sliceSize <= 0 {
   226  		lfc.sliceSize = DefaultBufSize
   227  	}
   228  	if lfc.bufSize < DefaultBufSize {
   229  		lfc.bufSize = DefaultBufSize
   230  	}
   231  }