|
6 | 6 | "encoding/xml" |
7 | 7 | "errors" |
8 | 8 | "fmt" |
| 9 | + "hash/crc64" |
9 | 10 | "io" |
10 | 11 | "io/ioutil" |
11 | 12 | "net/http" |
@@ -147,7 +148,7 @@ type ObjectPutHeaderOptions struct { |
147 | 148 | ContentEncoding string `header:"Content-Encoding,omitempty" url:"-"` |
148 | 149 | ContentType string `header:"Content-Type,omitempty" url:"-"` |
149 | 150 | ContentMD5 string `header:"Content-MD5,omitempty" url:"-"` |
150 | | - ContentLength int `header:"Content-Length,omitempty" url:"-"` |
| 151 | + ContentLength int64 `header:"Content-Length,omitempty" url:"-"` |
151 | 152 | ContentLanguage string `header:"Content-Language,omitempty" url:"-"` |
152 | 153 | Expect string `header:"Expect,omitempty" url:"-"` |
153 | 154 | Expires string `header:"Expires,omitempty" url:"-"` |
@@ -179,26 +180,27 @@ type ObjectPutOptions struct { |
179 | 180 |
|
180 | 181 | // Put Object请求可以将一个文件(Oject)上传至指定Bucket。 |
181 | 182 | // |
182 | | -// 当 r 不是 bytes.Buffer/bytes.Reader/strings.Reader 时,必须指定 opt.ObjectPutHeaderOptions.ContentLength |
183 | | -// |
184 | 183 | // https://www.qcloud.com/document/product/436/7749 |
185 | 184 | func (s *ObjectService) Put(ctx context.Context, name string, r io.Reader, opt *ObjectPutOptions) (*Response, error) { |
186 | 185 | if err := CheckReaderLen(r); err != nil { |
187 | 186 | return nil, err |
188 | 187 | } |
| 188 | + totalBytes, err := GetReaderLen(r) |
| 189 | + if err != nil && opt != nil && opt.Listener != nil { |
| 190 | + return nil, err |
| 191 | + } |
| 192 | + reader := TeeReader(r, nil, totalBytes, nil) |
| 193 | + if s.client.Conf.EnableCRC { |
| 194 | + reader.writer = crc64.New(crc64.MakeTable(crc64.ECMA)) |
| 195 | + } |
189 | 196 | if opt != nil && opt.Listener != nil { |
190 | | - totalBytes, err := GetReaderLen(r) |
191 | | - if err != nil { |
192 | | - return nil, err |
193 | | - } |
194 | | - r = TeeReader(r, nil, totalBytes, opt.Listener) |
| 197 | + reader.listener = opt.Listener |
195 | 198 | } |
196 | | - |
197 | 199 | sendOpt := sendOptions{ |
198 | 200 | baseURL: s.client.BaseURL.BucketURL, |
199 | 201 | uri: "/" + encodeURIComponent(name), |
200 | 202 | method: http.MethodPut, |
201 | | - body: r, |
| 203 | + body: reader, |
202 | 204 | optHeader: opt, |
203 | 205 | } |
204 | 206 | resp, err := s.client.send(ctx, &sendOpt) |
@@ -556,38 +558,54 @@ type Results struct { |
556 | 558 | err error |
557 | 559 | } |
558 | 560 |
|
| 561 | +func LimitReadCloser(r io.Reader, n int64) io.Reader { |
| 562 | + var lc LimitedReadCloser |
| 563 | + lc.R = r |
| 564 | + lc.N = n |
| 565 | + return &lc |
| 566 | +} |
| 567 | + |
| 568 | +type LimitedReadCloser struct { |
| 569 | + io.LimitedReader |
| 570 | +} |
| 571 | + |
| 572 | +func (lc *LimitedReadCloser) Close() error { |
| 573 | + if r, ok := lc.R.(io.ReadCloser); ok { |
| 574 | + return r.Close() |
| 575 | + } |
| 576 | + return nil |
| 577 | +} |
| 578 | + |
559 | 579 | func worker(s *ObjectService, jobs <-chan *Jobs, results chan<- *Results) { |
560 | 580 | for j := range jobs { |
561 | | - fd, err := os.Open(j.FilePath) |
562 | | - var res Results |
563 | | - if err != nil { |
564 | | - res.err = err |
565 | | - res.PartNumber = j.Chunk.Number |
566 | | - res.Resp = nil |
567 | | - results <- &res |
568 | | - } |
569 | | - |
570 | | - // UploadPart do not support the chunk trsf, so need to add the content-length |
571 | | - j.Opt.ContentLength = int(j.Chunk.Size) |
| 581 | + j.Opt.ContentLength = j.Chunk.Size |
572 | 582 |
|
573 | 583 | rt := j.RetryTimes |
574 | 584 | for { |
| 585 | + // http.Request.Body can be Closed in request |
| 586 | + fd, err := os.Open(j.FilePath) |
| 587 | + var res Results |
| 588 | + if err != nil { |
| 589 | + res.err = err |
| 590 | + res.PartNumber = j.Chunk.Number |
| 591 | + res.Resp = nil |
| 592 | + results <- &res |
| 593 | + break |
| 594 | + } |
575 | 595 | fd.Seek(j.Chunk.OffSet, os.SEEK_SET) |
576 | 596 | resp, err := s.UploadPart(context.Background(), j.Name, j.UploadId, j.Chunk.Number, |
577 | | - &io.LimitedReader{R: fd, N: j.Chunk.Size}, j.Opt) |
| 597 | + LimitReadCloser(fd, j.Chunk.Size), j.Opt) |
578 | 598 | res.PartNumber = j.Chunk.Number |
579 | 599 | res.Resp = resp |
580 | 600 | res.err = err |
581 | 601 | if err != nil { |
582 | 602 | rt-- |
583 | 603 | if rt == 0 { |
584 | | - fd.Close() |
585 | 604 | results <- &res |
586 | 605 | break |
587 | 606 | } |
588 | 607 | continue |
589 | 608 | } |
590 | | - fd.Close() |
591 | 609 | results <- &res |
592 | 610 | break |
593 | 611 | } |
|
0 commit comments