github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/pcsutil/checksum/checksum.go (about)

     1  // Package checksum 校验本地文件包
     2  package checksum
     3  
     4  import (
     5  	"crypto/md5"
     6  	"github.com/qjfoidnh/BaiduPCS-Go/pcsutil/cachepool"
     7  	"github.com/qjfoidnh/BaiduPCS-Go/pcsutil/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) GetSliceDataContent(offset, length int64) (dataContent []byte, readLength int64, err error) {
   124  	dataContent = make([]byte, length)
   125  	ret, err := lfc.file.ReadAt(dataContent, offset)
   126  	if err != nil && err != io.EOF {
   127  		return
   128  	}
   129  	readLength = int64(ret)
   130  	dataContent = dataContent[:ret]
   131  	return dataContent, readLength, nil
   132  }
   133  
   134  func (lfc *LocalFileChecksum) repeatRead(wus ...*ChecksumWriteUnit) (err error) {
   135  	if lfc.file == nil {
   136  		return ErrFileIsNil
   137  	}
   138  
   139  	lfc.initBuf()
   140  
   141  	defer func() {
   142  		_, err = lfc.file.Seek(0, os.SEEK_SET) // 恢复文件指针
   143  		if err != nil {
   144  			return
   145  		}
   146  	}()
   147  
   148  	// 读文件
   149  	var (
   150  		n int
   151  	)
   152  read:
   153  	for {
   154  		n, err = lfc.file.Read(lfc.buf)
   155  		switch err {
   156  		case io.EOF:
   157  			err = lfc.writeChecksum(lfc.buf[:n], wus...)
   158  			break read
   159  		case nil:
   160  			err = lfc.writeChecksum(lfc.buf[:n], wus...)
   161  		default:
   162  			return
   163  		}
   164  	}
   165  	switch err {
   166  	case ErrChecksumWriteAllStop: // 全部结束
   167  		err = nil
   168  	}
   169  	return
   170  }
   171  
   172  func (lfc *LocalFileChecksum) createChecksumWriteUnit(cw ChecksumWriter, isAll, isSlice bool, getSumFunc func(sliceSum interface{}, sum interface{})) (wu *ChecksumWriteUnit, deferFunc func(err error)) {
   173  	wu = &ChecksumWriteUnit{
   174  		ChecksumWriter: cw,
   175  		End:            lfc.LocalFileMeta.Length,
   176  		OnlySliceSum:   !isAll,
   177  	}
   178  
   179  	if isSlice {
   180  		wu.SliceEnd = int64(lfc.sliceSize)
   181  	}
   182  
   183  	return wu, func(err error) {
   184  		if err != nil {
   185  			return
   186  		}
   187  		getSumFunc(wu.SliceSum, wu.Sum)
   188  	}
   189  }
   190  
   191  // Sum 计算文件摘要值
   192  func (lfc *LocalFileChecksum) Sum(checkSumFlag int) (err error) {
   193  	lfc.fix()
   194  	wus := make([]*ChecksumWriteUnit, 0, 2)
   195  	if (checkSumFlag & (CHECKSUM_MD5 | CHECKSUM_SLICE_MD5)) != 0 {
   196  		md5w := md5.New()
   197  		wu, d := lfc.createChecksumWriteUnit(
   198  			NewHashChecksumWriter(md5w),
   199  			(checkSumFlag&CHECKSUM_MD5) != 0,
   200  			(checkSumFlag&CHECKSUM_SLICE_MD5) != 0,
   201  			func(sliceSum interface{}, sum interface{}) {
   202  				if sliceSum != nil {
   203  					lfc.SliceMD5 = sliceSum.([]byte)
   204  				}
   205  				if sum != nil {
   206  					lfc.MD5 = sum.([]byte)
   207  				}
   208  			},
   209  		)
   210  
   211  		wus = append(wus, wu)
   212  		defer d(err)
   213  	}
   214  	if (checkSumFlag & CHECKSUM_CRC32) != 0 {
   215  		crc32w := crc32.NewIEEE()
   216  		wu, d := lfc.createChecksumWriteUnit(
   217  			NewHash32ChecksumWriter(crc32w),
   218  			true,
   219  			false,
   220  			func(sliceSum interface{}, sum interface{}) {
   221  				if sum != nil {
   222  					lfc.CRC32 = sum.(uint32)
   223  				}
   224  			},
   225  		)
   226  
   227  		wus = append(wus, wu)
   228  		defer d(err)
   229  	}
   230  
   231  	err = lfc.repeatRead(wus...)
   232  	return
   233  }
   234  
   235  func (lfc *LocalFileChecksum) fix() {
   236  	if lfc.sliceSize <= 0 {
   237  		lfc.sliceSize = DefaultBufSize
   238  	}
   239  	if lfc.bufSize < DefaultBufSize {
   240  		lfc.bufSize = DefaultBufSize
   241  	}
   242  }