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  }