github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/reader_partial_file.go (about)

     1  package caches
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
     8  	rangeutils "github.com/TeaOSLab/EdgeNode/internal/utils/ranges"
     9  	"github.com/iwind/TeaGo/types"
    10  	"io"
    11  )
    12  
    13  type PartialFileReader struct {
    14  	*FileReader
    15  
    16  	ranges    *PartialRanges
    17  	rangePath string
    18  }
    19  
    20  func NewPartialFileReader(fp *fsutils.File) *PartialFileReader {
    21  	return &PartialFileReader{
    22  		FileReader: NewFileReader(fp),
    23  		rangePath:  PartialRangesFilePath(fp.Name()),
    24  	}
    25  }
    26  
    27  func (this *PartialFileReader) Init() error {
    28  	return this.InitAutoDiscard(true)
    29  }
    30  
    31  func (this *PartialFileReader) InitAutoDiscard(autoDiscard bool) error {
    32  	if this.openFile != nil {
    33  		this.meta = this.openFile.meta
    34  		this.header = this.openFile.header
    35  	}
    36  
    37  	var isOk = false
    38  
    39  	if autoDiscard {
    40  		defer func() {
    41  			if !isOk {
    42  				_ = this.discard()
    43  			}
    44  		}()
    45  	}
    46  
    47  	// 读取Range
    48  	ranges, err := NewPartialRangesFromFile(this.rangePath)
    49  	if err != nil {
    50  		return fmt.Errorf("read ranges failed: %w", err)
    51  	}
    52  	this.ranges = ranges
    53  
    54  	var buf = this.meta
    55  	if len(buf) == 0 {
    56  		buf = make([]byte, SizeMeta)
    57  		ok, readErr := this.readToBuff(this.fp, buf)
    58  		if readErr != nil {
    59  			return readErr
    60  		}
    61  		if !ok {
    62  			return ErrNotFound
    63  		}
    64  		this.meta = buf
    65  	}
    66  
    67  	this.expiresAt = int64(binary.BigEndian.Uint32(buf[:SizeExpiresAt]))
    68  
    69  	status := types.Int(string(buf[SizeExpiresAt : SizeExpiresAt+SizeStatus]))
    70  	if status < 100 || status > 999 {
    71  		return errors.New("invalid status")
    72  	}
    73  	this.status = status
    74  
    75  	// URL
    76  	var urlLength = binary.BigEndian.Uint32(buf[SizeExpiresAt+SizeStatus : SizeExpiresAt+SizeStatus+SizeURLLength])
    77  
    78  	// header
    79  	var headerSize = int(binary.BigEndian.Uint32(buf[SizeExpiresAt+SizeStatus+SizeURLLength : SizeExpiresAt+SizeStatus+SizeURLLength+SizeHeaderLength]))
    80  	if headerSize == 0 {
    81  		return nil
    82  	}
    83  	this.headerSize = headerSize
    84  	this.headerOffset = int64(SizeMeta) + int64(urlLength)
    85  
    86  	// body
    87  	this.bodyOffset = this.headerOffset + int64(headerSize)
    88  	bodySize := int(binary.BigEndian.Uint64(buf[SizeExpiresAt+SizeStatus+SizeURLLength+SizeHeaderLength : SizeExpiresAt+SizeStatus+SizeURLLength+SizeHeaderLength+SizeBodyLength]))
    89  	if bodySize == 0 {
    90  		isOk = true
    91  		return nil
    92  	}
    93  	this.bodySize = int64(bodySize)
    94  
    95  	// read header
    96  	if this.openFileCache != nil && len(this.header) == 0 {
    97  		if headerSize > 0 && headerSize <= 512 {
    98  			this.header = make([]byte, headerSize)
    99  			_, err = this.fp.Seek(this.headerOffset, io.SeekStart)
   100  			if err != nil {
   101  				return err
   102  			}
   103  			_, err = this.readToBuff(this.fp, this.header)
   104  			if err != nil {
   105  				return err
   106  			}
   107  		}
   108  	}
   109  
   110  	isOk = true
   111  
   112  	return nil
   113  }
   114  
   115  // ContainsRange 是否包含某些区间内容
   116  // 这里的 r 是已经经过格式化的
   117  func (this *PartialFileReader) ContainsRange(r rangeutils.Range) (r2 rangeutils.Range, ok bool) {
   118  	r2, ok = this.ranges.Nearest(r.Start(), r.End())
   119  	if ok && this.bodySize > 0 {
   120  		// 考虑可配置
   121  		const minSpan = 128 << 10
   122  
   123  		// 这里限制返回的最小缓存,防止因为返回的内容过小而导致请求过多
   124  		if r2.Length() < r.Length() && r2.Length() < minSpan {
   125  			ok = false
   126  		}
   127  	}
   128  	return
   129  }
   130  
   131  // MaxLength 获取区间最大长度
   132  func (this *PartialFileReader) MaxLength() int64 {
   133  	if this.bodySize > 0 {
   134  		return this.bodySize
   135  	}
   136  	return this.ranges.Max() + 1
   137  }
   138  
   139  func (this *PartialFileReader) Ranges() *PartialRanges {
   140  	return this.ranges
   141  }
   142  
   143  func (this *PartialFileReader) IsCompleted() bool {
   144  	return this.ranges != nil && this.ranges.IsCompleted()
   145  }
   146  
   147  func (this *PartialFileReader) discard() error {
   148  	SharedPartialRangesQueue.Delete(this.rangePath)
   149  	_ = fsutils.Remove(this.rangePath)
   150  
   151  	return this.FileReader.discard()
   152  }