github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/downloader/destination/destination.go (about) 1 package destination 2 3 import ( 4 "errors" 5 "io" 6 "os" 7 "path/filepath" 8 9 internal_io "github.com/qiniu/go-sdk/v7/internal/io" 10 resumablerecorder "github.com/qiniu/go-sdk/v7/storagev2/downloader/resumable_recorder" 11 ) 12 13 type ( 14 // 切片选项 15 SplitOptions struct { 16 // 只读可恢复记录仪介质 17 Medium resumablerecorder.ReadableResumableRecorderMedium 18 } 19 20 // 数据目标 21 Destination interface { 22 PartWriter 23 io.Closer 24 25 // 切片 26 Split(totalSize, partSize uint64, options *SplitOptions) ([]Part, error) 27 28 // 数据目标 ID 29 DestinationID() (string, error) 30 31 // 获取文件,如果数据源不是文件,则返回 nil 32 GetFile() *os.File 33 } 34 35 // 分片 36 Part interface { 37 PartWriter 38 39 // 分片大小 40 Size() uint64 41 42 // 分片偏移量 43 Offset() uint64 44 45 // 分片偏移量 46 HaveDownloaded() uint64 47 } 48 49 // 分片写入接口 50 PartWriter interface { 51 // 从 `io.Reader` 复制数据写入 52 CopyFrom(io.Reader, func(uint64)) (uint64, error) 53 } 54 55 writerAtDestination struct { 56 wr WriteAtCloser 57 destinationID string 58 } 59 60 writerAtPart struct { 61 *internal_io.OffsetWriter 62 offset, totalSize, restSize uint64 63 } 64 65 writeCloserDestination struct { 66 wr io.WriteCloser 67 destinationID string 68 } 69 70 writeCloserPart struct { 71 wr io.Writer 72 totalSize, restSize uint64 73 } 74 75 WriteAtCloser interface { 76 io.WriterAt 77 io.WriteSeeker 78 io.Closer 79 } 80 ) 81 82 // 将 io.WriteCloser 封装为数据目标 83 func NewWriteCloserDestination(wr io.WriteCloser, destinationID string) Destination { 84 return &writeCloserDestination{wr, destinationID} 85 } 86 87 func (wcd *writeCloserDestination) CopyFrom(r io.Reader, progress func(uint64)) (uint64, error) { 88 return copyBuffer(wcd.wr, r, progress) 89 } 90 91 func (wcd *writeCloserDestination) Split(totalSize, _ uint64, _ *SplitOptions) ([]Part, error) { 92 return []Part{&writeCloserPart{wcd.wr, totalSize, totalSize}}, nil 93 } 94 95 func (wcd *writeCloserDestination) DestinationID() (string, error) { 96 return wcd.destinationID, nil 97 } 98 99 func (wcd *writeCloserDestination) Close() error { 100 return wcd.wr.Close() 101 } 102 103 func (wcd *writeCloserDestination) GetFile() *os.File { 104 if file, ok := wcd.wr.(*os.File); ok { 105 return file 106 } else { 107 return nil 108 } 109 } 110 111 func (wcp *writeCloserPart) Size() uint64 { 112 return wcp.totalSize 113 } 114 115 func (wcp *writeCloserPart) Offset() uint64 { 116 return 0 117 } 118 119 func (wcp *writeCloserPart) HaveDownloaded() uint64 { 120 return 0 121 } 122 123 var errInvalidWrite = errors.New("invalid write result") 124 125 func (wcp *writeCloserPart) CopyFrom(r io.Reader, progress func(uint64)) (uint64, error) { 126 var newProgress func(uint64) 127 128 if wcp.restSize == 0 { 129 return 0, nil 130 } 131 132 haveCopied := wcp.HaveDownloaded() 133 if progress != nil { 134 newProgress = func(downloaded uint64) { progress(haveCopied + downloaded) } 135 } 136 n, err := copyBuffer(wcp.wr, io.LimitReader(r, int64(wcp.restSize)), newProgress) 137 if n > 0 { 138 wcp.restSize -= n 139 } 140 return n, err 141 } 142 143 // 将 io.WriterAt + io.WriteSeeker + io.Closer 封装为数据目标 144 func NewWriteAtCloserDestination(wr WriteAtCloser, destinationID string) Destination { 145 return &writerAtDestination{wr, destinationID} 146 } 147 148 func (wad *writerAtDestination) CopyFrom(r io.Reader, progress func(uint64)) (uint64, error) { 149 n, err := io.Copy(wad.wr, r) 150 return uint64(n), err 151 } 152 153 func (wad *writerAtDestination) Split(totalSize, partSize uint64, options *SplitOptions) ([]Part, error) { 154 var ( 155 parts []Part 156 offsetMap = make(map[uint64]uint64) 157 resumableRecord resumablerecorder.ResumableRecord 158 err error 159 ) 160 if options == nil { 161 options = &SplitOptions{} 162 } 163 164 if medium := options.Medium; medium != nil { 165 for { 166 if err = medium.Next(&resumableRecord); err != nil { 167 break 168 } 169 offsetMap[resumableRecord.Offset] = resumableRecord.PartWritten 170 } 171 } 172 173 parts = make([]Part, 0, (totalSize+partSize-1)/partSize) 174 for offset := uint64(0); offset < totalSize; offset += partSize { 175 size := partSize 176 if size > (totalSize - offset) { 177 size = totalSize - offset 178 } 179 haveWritten := offsetMap[offset] 180 parts = append(parts, &writerAtPart{internal_io.NewOffsetWriter(wad.wr, int64(offset+haveWritten)), offset, size, size - haveWritten}) 181 } 182 return parts, nil 183 } 184 185 func (wad *writerAtDestination) DestinationID() (string, error) { 186 return wad.destinationID, nil 187 } 188 189 func (wad *writerAtDestination) Close() error { 190 return wad.wr.Close() 191 } 192 193 func (wad *writerAtDestination) GetFile() *os.File { 194 if file, ok := wad.wr.(*os.File); ok { 195 return file 196 } else { 197 return nil 198 } 199 } 200 201 func (w *writerAtPart) Size() uint64 { 202 return w.totalSize 203 } 204 205 func (w *writerAtPart) Offset() uint64 { 206 return w.offset 207 } 208 209 func (w *writerAtPart) HaveDownloaded() uint64 { 210 return w.totalSize - w.restSize 211 } 212 213 func (w *writerAtPart) CopyFrom(r io.Reader, progress func(uint64)) (uint64, error) { 214 var newProgress func(uint64) 215 216 if w.restSize == 0 { 217 return 0, nil 218 } 219 220 haveCopied := w.HaveDownloaded() 221 if progress != nil { 222 newProgress = func(downloaded uint64) { progress(haveCopied + downloaded) } 223 } 224 n, err := copyBuffer(w.OffsetWriter, io.LimitReader(r, int64(w.restSize)), newProgress) 225 if n > 0 { 226 w.restSize -= n 227 } 228 return n, err 229 } 230 231 func copyBuffer(w io.Writer, r io.Reader, progress func(uint64)) (uint64, error) { 232 const BUFSIZE = 32 * 1024 233 var ( 234 buf = make([]byte, BUFSIZE) 235 haveCopied uint64 236 nr, nw int 237 er, ew, err error 238 ) 239 for { 240 nr, er = r.Read(buf) 241 if nr > 0 { 242 nw, ew = w.Write(buf[0:nr]) 243 if nw < 0 || nr < nw { 244 nw = 0 245 if ew == nil { 246 ew = errInvalidWrite 247 } 248 } 249 haveCopied += uint64(nw) 250 if ew != nil { 251 err = ew 252 break 253 } 254 if nr != nw { 255 err = io.ErrShortWrite 256 break 257 } 258 if progress != nil { 259 progress(haveCopied) 260 } 261 } 262 if er != nil { 263 if er != io.EOF { 264 err = er 265 } 266 break 267 } 268 } 269 return haveCopied, err 270 } 271 272 // 将文件封装为数据目标 273 func NewFileDestination(filePath string) (Destination, error) { 274 file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0600) 275 if err != nil { 276 return nil, err 277 } 278 if !canSeekReally(file) { 279 return NewWriteCloserDestination(file, ""), nil 280 } else if absFilePath, err := filepath.Abs(filePath); err != nil { 281 return nil, err 282 } else { 283 return NewWriteAtCloserDestination(file, absFilePath), nil 284 } 285 } 286 287 func canSeekReally(seeker io.Seeker) bool { 288 _, err := seeker.Seek(0, io.SeekCurrent) 289 return err == nil 290 }