mirror of
https://github.com/tickstep/aliyunpan.git
synced 2025-01-23 22:42:15 +08:00
272 lines
5.8 KiB
Go
272 lines
5.8 KiB
Go
// Copyright (c) 2020 tickstep.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
package localfile
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"crypto/sha1"
|
|
"encoding/hex"
|
|
"github.com/tickstep/aliyunpan-api/aliyunpan"
|
|
"hash/crc32"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/tickstep/library-go/cachepool"
|
|
"github.com/tickstep/library-go/converter"
|
|
)
|
|
|
|
const (
|
|
// DefaultBufSize 默认的bufSize
|
|
DefaultBufSize = int(256 * converter.KB)
|
|
)
|
|
|
|
const (
|
|
// CHECKSUM_MD5 获取文件的 md5 值
|
|
CHECKSUM_MD5 int = 1 << iota
|
|
|
|
// CHECKSUM_CRC32 获取文件的 crc32 值
|
|
CHECKSUM_CRC32
|
|
|
|
// CHECKSUM_SHA1 获取文件的 sha1 值
|
|
CHECKSUM_SHA1
|
|
)
|
|
|
|
type (
|
|
// LocalFileMeta 本地文件元信息
|
|
LocalFileMeta struct {
|
|
Path SymlinkFile `json:"path,omitempty"` // 本地路径
|
|
Length int64 `json:"length,omitempty"` // 文件大小
|
|
MD5 string `json:"md5,omitempty"` // 文件的 md5
|
|
CRC32 uint32 `json:"crc32,omitempty"` // 文件的 crc32
|
|
SHA1 string `json:"sha1,omitempty"` // 文件的 sha1
|
|
ModTime int64 `json:"modtime"` // 修改日期
|
|
|
|
// 网盘上传参数
|
|
UploadOpEntity *aliyunpan.CreateFileUploadResult `json:"uploadOpEntity"`
|
|
|
|
// ParentFolderId 存储云盘的目录ID
|
|
ParentFolderId string `json:"parent_folder_id,omitempty"`
|
|
}
|
|
|
|
// LocalFileEntity 校验本地文件
|
|
LocalFileEntity struct {
|
|
LocalFileMeta
|
|
bufSize int
|
|
buf []byte
|
|
file *os.File // 文件
|
|
}
|
|
)
|
|
|
|
func NewLocalSymlinkFileEntity(file SymlinkFile) *LocalFileEntity {
|
|
return NewLocalFileEntityWithBufSize(file, DefaultBufSize)
|
|
}
|
|
|
|
func NewLocalFileEntity(localPath string) *LocalFileEntity {
|
|
return NewLocalFileEntityWithBufSize(NewSymlinkFile(localPath), DefaultBufSize)
|
|
}
|
|
|
|
func NewLocalFileEntityWithBufSize(file SymlinkFile, bufSize int) *LocalFileEntity {
|
|
return &LocalFileEntity{
|
|
LocalFileMeta: LocalFileMeta{
|
|
Path: file,
|
|
},
|
|
bufSize: bufSize,
|
|
}
|
|
}
|
|
|
|
// OpenPath 检查文件状态并获取文件的大小 (Length)
|
|
func (lfc *LocalFileEntity) OpenPath() error {
|
|
if lfc.file != nil {
|
|
lfc.file.Close()
|
|
}
|
|
|
|
var err error
|
|
lfc.file, err = os.Open(lfc.Path.RealPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
info, err := lfc.file.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
lfc.Length = info.Size()
|
|
lfc.ModTime = info.ModTime().Unix()
|
|
return nil
|
|
}
|
|
|
|
// GetFile 获取文件
|
|
func (lfc *LocalFileEntity) GetFile() *os.File {
|
|
return lfc.file
|
|
}
|
|
|
|
// Close 关闭文件
|
|
func (lfc *LocalFileEntity) Close() error {
|
|
if lfc.file == nil {
|
|
return ErrFileIsNil
|
|
}
|
|
|
|
return lfc.file.Close()
|
|
}
|
|
|
|
func (lfc *LocalFileEntity) initBuf() {
|
|
if lfc.buf == nil {
|
|
lfc.buf = cachepool.RawMallocByteSlice(lfc.bufSize)
|
|
}
|
|
}
|
|
|
|
func (lfc *LocalFileEntity) writeChecksum(data []byte, wus ...*ChecksumWriteUnit) (err error) {
|
|
doneCount := 0
|
|
for _, wu := range wus {
|
|
_, err := wu.Write(data)
|
|
switch err {
|
|
case ErrChecksumWriteStop:
|
|
doneCount++
|
|
continue
|
|
case nil:
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
if doneCount == len(wus) {
|
|
return ErrChecksumWriteAllStop
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (lfc *LocalFileEntity) repeatRead(wus ...*ChecksumWriteUnit) (err error) {
|
|
if lfc.file == nil {
|
|
return ErrFileIsNil
|
|
}
|
|
|
|
lfc.initBuf()
|
|
|
|
defer func() {
|
|
_, err = lfc.file.Seek(0, os.SEEK_SET) // 恢复文件指针
|
|
if err != nil {
|
|
return
|
|
}
|
|
}()
|
|
|
|
// 读文件
|
|
var (
|
|
n int
|
|
)
|
|
read:
|
|
for {
|
|
n, err = lfc.file.Read(lfc.buf)
|
|
switch err {
|
|
case io.EOF:
|
|
err = lfc.writeChecksum(lfc.buf[:n], wus...)
|
|
break read
|
|
case nil:
|
|
err = lfc.writeChecksum(lfc.buf[:n], wus...)
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
switch err {
|
|
case ErrChecksumWriteAllStop: // 全部结束
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func (lfc *LocalFileEntity) createChecksumWriteUnit(cw ChecksumWriter, isAll bool, getSumFunc func(sum interface{})) (wu *ChecksumWriteUnit, deferFunc func(err error)) {
|
|
wu = &ChecksumWriteUnit{
|
|
ChecksumWriter: cw,
|
|
End: lfc.LocalFileMeta.Length,
|
|
OnlySliceSum: !isAll,
|
|
}
|
|
|
|
return wu, func(err error) {
|
|
if err != nil {
|
|
return
|
|
}
|
|
getSumFunc(wu.Sum)
|
|
}
|
|
}
|
|
|
|
// Sum 计算文件摘要值
|
|
func (lfc *LocalFileEntity) Sum(checkSumFlag int) (err error) {
|
|
lfc.fix()
|
|
wus := make([]*ChecksumWriteUnit, 0, 2)
|
|
if (checkSumFlag & (CHECKSUM_MD5)) != 0 {
|
|
md5w := md5.New()
|
|
wu, d := lfc.createChecksumWriteUnit(
|
|
NewHashChecksumWriter(md5w),
|
|
(checkSumFlag&CHECKSUM_MD5) != 0,
|
|
func(sum interface{}) {
|
|
if sum != nil {
|
|
lfc.MD5 = hex.EncodeToString(sum.([]byte))
|
|
}
|
|
|
|
// zero size file
|
|
if lfc.Length == 0 {
|
|
lfc.MD5 = aliyunpan.DefaultZeroSizeFileContentHash
|
|
}
|
|
},
|
|
)
|
|
|
|
wus = append(wus, wu)
|
|
defer d(err)
|
|
}
|
|
if (checkSumFlag & CHECKSUM_CRC32) != 0 {
|
|
crc32w := crc32.NewIEEE()
|
|
wu, d := lfc.createChecksumWriteUnit(
|
|
NewHash32ChecksumWriter(crc32w),
|
|
true,
|
|
func(sum interface{}) {
|
|
if sum != nil {
|
|
lfc.CRC32 = sum.(uint32)
|
|
}
|
|
},
|
|
)
|
|
|
|
wus = append(wus, wu)
|
|
defer d(err)
|
|
}
|
|
if (checkSumFlag & (CHECKSUM_SHA1)) != 0 {
|
|
sha1w := sha1.New()
|
|
wu, d := lfc.createChecksumWriteUnit(
|
|
NewHashChecksumWriter(sha1w),
|
|
(checkSumFlag&CHECKSUM_SHA1) != 0,
|
|
func(sum interface{}) {
|
|
if sum != nil {
|
|
lfc.SHA1 = strings.ToUpper(hex.EncodeToString(sum.([]byte)))
|
|
}
|
|
|
|
// zero size file
|
|
if lfc.Length == 0 {
|
|
lfc.SHA1 = aliyunpan.DefaultZeroSizeFileContentHash
|
|
}
|
|
},
|
|
)
|
|
|
|
wus = append(wus, wu)
|
|
defer d(err)
|
|
}
|
|
|
|
err = lfc.repeatRead(wus...)
|
|
return
|
|
}
|
|
|
|
func (lfc *LocalFileEntity) fix() {
|
|
if lfc.bufSize < DefaultBufSize {
|
|
lfc.bufSize = DefaultBufSize
|
|
}
|
|
}
|