You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
2.3 KiB
121 lines
2.3 KiB
package uploader |
|
|
|
import ( |
|
"context" |
|
"io" |
|
|
|
"github.com/go-faster/errors" |
|
|
|
"github.com/gotd/td/bin" |
|
"github.com/gotd/td/internal/syncio" |
|
"github.com/gotd/td/internal/tdsync" |
|
"github.com/gotd/td/tg" |
|
"github.com/gotd/td/tgerr" |
|
) |
|
|
|
type part struct { |
|
id int |
|
buf *bin.Buffer |
|
upload *Upload |
|
} |
|
|
|
func (u *Uploader) uploadBigFilePart(ctx context.Context, p part) (int, error) { |
|
defer u.pool.Put(p.buf) |
|
|
|
// Upload loop. |
|
for { |
|
r, err := u.rpc.UploadSaveBigFilePart(ctx, &tg.UploadSaveBigFilePartRequest{ |
|
FileID: p.upload.id, |
|
FilePart: p.id, |
|
FileTotalParts: p.upload.totalParts, |
|
Bytes: p.buf.Buf, |
|
}) |
|
|
|
if flood, err := tgerr.FloodWait(ctx, err); err != nil { |
|
if flood { |
|
continue |
|
} |
|
return 0, errors.Wrapf(err, "send upload part %d RPC", p.id) |
|
} |
|
|
|
// If Telegram returned false, it seems save is not successful, so we retry to send. |
|
if r { |
|
return p.buf.Len(), nil |
|
} |
|
} |
|
} |
|
|
|
func (u *Uploader) bigLoop(ctx context.Context, threads int, upload *Upload) error { // nolint:gocognit |
|
g := tdsync.NewCancellableGroup(ctx) |
|
toSend := make(chan part, threads) |
|
|
|
// Run read loop |
|
r := syncio.NewReader(upload.from) |
|
g.Go(func(ctx context.Context) error { |
|
last := false |
|
|
|
for { |
|
buf := u.pool.GetSize(u.partSize) |
|
|
|
n, err := io.ReadFull(r, buf.Buf) |
|
switch { |
|
case errors.Is(err, io.ErrUnexpectedEOF): |
|
last = true |
|
case errors.Is(err, io.EOF): |
|
u.pool.Put(buf) |
|
|
|
close(toSend) |
|
return nil |
|
case err != nil: |
|
u.pool.Put(buf) |
|
|
|
return errors.Wrap(err, "read source") |
|
} |
|
|
|
buf.Buf = buf.Buf[:n] |
|
nextPart := part{ |
|
id: int(upload.sentParts.Load()), |
|
buf: buf, |
|
upload: upload, |
|
} |
|
select { |
|
case toSend <- nextPart: |
|
upload.sentParts.Inc() |
|
if last { |
|
close(toSend) |
|
return nil |
|
} |
|
case <-ctx.Done(): |
|
u.pool.Put(buf) |
|
|
|
return ctx.Err() |
|
} |
|
} |
|
}) |
|
|
|
for i := 0; i < threads; i++ { |
|
g.Go(func(ctx context.Context) error { |
|
for { |
|
select { |
|
case <-ctx.Done(): |
|
return ctx.Err() |
|
case part, ok := <-toSend: |
|
if !ok { |
|
return nil |
|
} |
|
|
|
n, err := u.uploadBigFilePart(ctx, part) |
|
if err != nil { |
|
return errors.Wrap(err, "upload part") |
|
} |
|
|
|
if err := u.callback(ctx, upload.confirm(part.id, n)); err != nil { |
|
return errors.Wrap(err, "progress callback") |
|
} |
|
} |
|
} |
|
}) |
|
} |
|
|
|
return g.Wait() |
|
}
|
|
|