github.com/iikira/iikira-go-utils@v0.0.0-20230610031953-f2cb11cde33a/requester/downloader/instance_state.go (about) 1 package downloader 2 3 import ( 4 "errors" 5 "github.com/golang/protobuf/proto" 6 "github.com/iikira/iikira-go-utils/pcsverbose" 7 "github.com/iikira/iikira-go-utils/requester/transfer" 8 "github.com/iikira/iikira-go-utils/utils/cachepool" 9 "github.com/json-iterator/go" 10 "os" 11 "sync" 12 ) 13 14 type ( 15 //InstanceState 状态, 断点续传信息 16 InstanceState struct { 17 saveFile *os.File 18 format InstanceStateStorageFormat 19 ii transfer.DownloadInstanceInfoExporter 20 mu sync.Mutex 21 } 22 23 // InstanceStateStorageFormat 断点续传储存类型 24 InstanceStateStorageFormat int 25 ) 26 27 const ( 28 // InstanceStateStorageFormatJSON json 格式 29 InstanceStateStorageFormatJSON = iota 30 // InstanceStateStorageFormatProto3 protobuf 格式 31 InstanceStateStorageFormatProto3 32 ) 33 34 //NewInstanceState 初始化InstanceState 35 func NewInstanceState(saveFile *os.File, format InstanceStateStorageFormat) *InstanceState { 36 return &InstanceState{ 37 saveFile: saveFile, 38 format: format, 39 } 40 } 41 42 func (is *InstanceState) checkSaveFile() bool { 43 return is.saveFile != nil 44 } 45 46 func (is *InstanceState) getSaveFileContents() []byte { 47 if !is.checkSaveFile() { 48 return nil 49 } 50 51 finfo, err := is.saveFile.Stat() 52 if err != nil { 53 panic(err) 54 } 55 56 size := finfo.Size() 57 if size > 0xffffffff { 58 panic("savePath too large") 59 } 60 intSize := int(size) 61 62 buf := cachepool.RawMallocByteSlice(intSize) 63 64 n, _ := is.saveFile.ReadAt(buf, 0) 65 return buf[:n] 66 } 67 68 //Get 获取断点续传信息 69 func (is *InstanceState) Get() (eii *transfer.DownloadInstanceInfo) { 70 if !is.checkSaveFile() { 71 return nil 72 } 73 74 is.mu.Lock() 75 defer is.mu.Unlock() 76 77 contents := is.getSaveFileContents() 78 if len(contents) <= 0 { 79 return 80 } 81 82 is.ii = &transfer.DownloadInstanceInfoExport{} 83 var err error 84 switch is.format { 85 case InstanceStateStorageFormatProto3: 86 err = proto.Unmarshal(contents, is.ii.(*transfer.DownloadInstanceInfoExport)) 87 default: 88 err = jsoniter.Unmarshal(contents, is.ii) 89 } 90 91 if err != nil { 92 pcsverbose.Verbosef("DEBUG: InstanceInfo unmarshal error: %s\n", err) 93 return 94 } 95 96 eii = is.ii.GetInstanceInfo() 97 return 98 } 99 100 //Put 提交断点续传信息 101 func (is *InstanceState) Put(eii *transfer.DownloadInstanceInfo) { 102 if !is.checkSaveFile() { 103 return 104 } 105 106 is.mu.Lock() 107 defer is.mu.Unlock() 108 109 if is.ii == nil { 110 is.ii = &transfer.DownloadInstanceInfoExport{} 111 } 112 is.ii.SetInstanceInfo(eii) 113 var ( 114 data []byte 115 err error 116 ) 117 switch is.format { 118 case InstanceStateStorageFormatProto3: 119 data, err = proto.Marshal(is.ii.(*transfer.DownloadInstanceInfoExport)) 120 default: 121 data, err = jsoniter.Marshal(is.ii) 122 } 123 if err != nil { 124 panic(err) 125 } 126 127 err = is.saveFile.Truncate(int64(len(data))) 128 if err != nil { 129 pcsverbose.Verbosef("DEBUG: truncate file error: %s\n", err) 130 } 131 132 _, err = is.saveFile.WriteAt(data, 0) 133 if err != nil { 134 pcsverbose.Verbosef("DEBUG: write instance state error: %s\n", err) 135 } 136 } 137 138 //Close 关闭 139 func (is *InstanceState) Close() error { 140 if !is.checkSaveFile() { 141 return nil 142 } 143 144 return is.saveFile.Close() 145 } 146 147 func (der *Downloader) initInstanceState(format InstanceStateStorageFormat) (err error) { 148 if der.instanceState != nil { 149 return errors.New("already initInstanceState") 150 } 151 152 var saveFile *os.File 153 if !der.config.IsTest && der.config.InstanceStatePath != "" { 154 saveFile, err = os.OpenFile(der.config.InstanceStatePath, os.O_RDWR|os.O_CREATE, 0777) 155 if err != nil { 156 return err 157 } 158 } 159 160 der.instanceState = NewInstanceState(saveFile, format) 161 return nil 162 } 163 164 func (der *Downloader) removeInstanceState() error { 165 der.instanceState.Close() 166 if !der.config.IsTest && der.config.InstanceStatePath != "" { 167 return os.Remove(der.config.InstanceStatePath) 168 } 169 return nil 170 }