Skip to content

Commit 458a674

Browse files
committed
add Name() func to get download path, and clean clean internal functions.
1 parent 54d5c74 commit 458a674

File tree

1 file changed

+122
-116
lines changed

1 file changed

+122
-116
lines changed

download.go

Lines changed: 122 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type (
3232

3333
Concurrency uint
3434

35-
URL, Dir, Name, Dest string
35+
URL, Dir, Dest string
3636

3737
Interval, ChunkSize, MinChunkSize, MaxChunkSize uint64
3838

@@ -42,6 +42,8 @@ type (
4242

4343
size, lastSize uint64
4444

45+
name string
46+
4547
info *Info
4648

4749
chunks []Chunk
@@ -91,7 +93,7 @@ func (d *Download) Init() (err error) {
9193

9294
// Set default client.
9395
if d.Client == nil {
94-
d.Client = GetDefaultClient()
96+
d.Client = DefaultClient
9597
}
9698

9799
// Set default context.
@@ -104,73 +106,19 @@ func (d *Download) Init() (err error) {
104106
return err
105107
}
106108

107-
// Set default dest path.
108-
if d.Dest == "" {
109-
110-
fname := d.info.Name
111-
112-
// if info name invalid get name from url.
113-
if fname == "" {
114-
fname = GetFilename(d.URL)
115-
}
116-
117-
d.Dest = filepath.Join(d.Dir, fname)
118-
}
119-
120109
// Partial content not supported 😢!
121110
if d.info.Rangeable == false || d.info.Size == 0 {
122111
return nil
123112
}
124113

125114
// Set concurrency default.
126115
if d.Concurrency == 0 {
127-
128-
d.Concurrency = uint(runtime.NumCPU() * 3)
129-
130-
// Set default max concurrency to 20.
131-
if d.Concurrency > 20 {
132-
d.Concurrency = 20
133-
}
134-
135-
// Set default min concurrency to 4.
136-
if d.Concurrency <= 2 {
137-
d.Concurrency = 4
138-
}
116+
d.Concurrency = getDefaultConcurrency()
139117
}
140118

141119
// Set default chunk size
142120
if d.ChunkSize == 0 {
143-
144-
d.ChunkSize = d.info.Size / uint64(d.Concurrency)
145-
146-
// if chunk size >= 102400000 bytes set default to (ChunkSize / 2)
147-
if d.ChunkSize >= 102400000 {
148-
d.ChunkSize = d.ChunkSize / 2
149-
}
150-
151-
// Set default min chunk size to 2m, or file size / 2
152-
if d.MinChunkSize == 0 {
153-
154-
d.MinChunkSize = 2000000
155-
156-
if d.MinChunkSize >= d.info.Size {
157-
d.MinChunkSize = d.info.Size / 2
158-
}
159-
}
160-
161-
// if Chunk size < Min size set chunk size to min.
162-
if d.ChunkSize < d.MinChunkSize {
163-
d.ChunkSize = d.MinChunkSize
164-
}
165-
166-
// Change ChunkSize if MaxChunkSize are set and ChunkSize > Max size
167-
if d.MaxChunkSize > 0 && d.ChunkSize > d.MaxChunkSize {
168-
d.ChunkSize = d.MaxChunkSize
169-
}
170-
171-
} else if d.ChunkSize >= d.info.Size {
172-
173-
d.ChunkSize = d.info.Size / 2
121+
d.ChunkSize = getDefaultChunkSize(d.info.Size, d.MinChunkSize, d.MaxChunkSize, uint64(d.Concurrency))
174122
}
175123

176124
var i, startRange, endRange, chunksLen uint64
@@ -211,74 +159,51 @@ func (d *Download) Init() (err error) {
211159
}
212160

213161
// Start downloads the file chunks, and merges them.
214-
func (d *Download) Start() error {
215-
216-
var (
217-
err error
218-
temp string
219-
)
220-
221-
// Create a new temp dir for this download.
222-
if temp, err = ioutil.TempDir("", "GotChunks"); err != nil {
223-
return err
224-
}
225-
226-
done := make(chan struct{}, 1)
227-
errs := make(chan error, 1)
162+
func (d *Download) Start() (err error) {
228163

229-
defer func() {
230-
231-
// Close channels.
232-
close(done)
233-
close(errs)
234-
// Remove temp dir.
235-
os.RemoveAll(temp)
236-
}()
237-
238-
// Partial content not supported, just download the file in one chunk.
164+
// Partial content not supported,
165+
// just download the file in one chunk.
239166
if len(d.chunks) == 0 {
240167

241-
file, err := os.Create(d.Dest)
168+
file, err := os.Create(d.Name())
242169

243170
if err != nil {
244171
return err
245172
}
246173

247174
defer file.Close()
248175

249-
return d.DownloadChunk(d.ctx, Chunk{}, file)
176+
return d.DownloadChunk(Chunk{}, file)
250177
}
251178

252-
go func() {
253-
select {
254-
case <-d.ctx.Done():
255-
// System or user interrupted the program
256-
errs <- ErrDownloadAborted
257-
return
258-
case <-done:
259-
// Everything went ok, no interruptions
260-
return
261-
}
262-
}()
179+
var (
180+
temp string
181+
done = make(chan struct{}, 1)
182+
errs = make(chan error, 1)
183+
)
184+
185+
// Create a new temp dir for this download.
186+
if temp, err = ioutil.TempDir("", "GotChunks"); err != nil {
187+
return err
188+
}
189+
defer os.RemoveAll(temp)
263190

264191
// Download chunks.
265-
go d.dl(d.ctx, temp, errs)
192+
go d.dl(temp, errs)
266193

267194
// Merge chunks.
268195
go func() {
269-
errs <- d.merge(d.ctx)
196+
errs <- d.merge()
270197
}()
271198

272-
// Wait for chunks...
273-
if err := <-errs; err != nil {
274-
275-
// Remove dest file on error.
276-
os.Remove(d.Dest)
277-
278-
return err
199+
select {
200+
case <-done:
201+
case err = <-errs:
202+
case <-d.ctx.Done():
203+
err = d.ctx.Err()
279204
}
280205

281-
return nil
206+
return
282207
}
283208

284209
// RunProgress runs ProgressFunc based on Interval and updates lastSize.
@@ -363,7 +288,7 @@ func (d Download) IsRangeable() bool {
363288
}
364289

365290
// Download chunks
366-
func (d *Download) dl(ctx context.Context, temp string, errc chan error) {
291+
func (d *Download) dl(temp string, errc chan error) {
367292

368293
var (
369294
// Wait group.
@@ -394,7 +319,7 @@ func (d *Download) dl(ctx context.Context, temp string, errc chan error) {
394319
defer chunk.Close()
395320

396321
// Download chunk.
397-
if err = d.DownloadChunk(ctx, d.chunks[i], chunk); err != nil {
322+
if err = d.DownloadChunk(d.chunks[i], chunk); err != nil {
398323
errc <- err
399324
return
400325
}
@@ -413,9 +338,9 @@ func (d *Download) dl(ctx context.Context, temp string, errc chan error) {
413338
}
414339

415340
// Merge downloaded chunks.
416-
func (d *Download) merge(ctx context.Context) error {
341+
func (d *Download) merge() error {
417342

418-
file, err := os.Create(d.Dest)
343+
file, err := os.Create(d.Name())
419344
if err != nil {
420345
return err
421346
}
@@ -425,9 +350,9 @@ func (d *Download) merge(ctx context.Context) error {
425350
for i := range d.chunks {
426351

427352
select {
353+
case <-d.ctx.Done():
354+
return d.ctx.Err()
428355
case <-d.chunks[i].Done:
429-
case <-ctx.Done():
430-
return ctx.Err()
431356
}
432357

433358
chunk, err := os.Open(d.chunks[i].Path)
@@ -452,16 +377,42 @@ func (d *Download) merge(ctx context.Context) error {
452377
return nil
453378
}
454379

380+
// Name returns the downloaded file path.
381+
func (d *Download) Name() string {
382+
383+
// Avoid returning different path at runtime.
384+
if d.name == "" {
385+
386+
fileName := d.Dest
387+
388+
// Set default file name.
389+
if fileName == "" {
390+
391+
// Content disposition name.
392+
fileName = d.info.Name
393+
394+
// if name invalid get name from url.
395+
if fileName == "" {
396+
fileName = GetFilename(d.URL)
397+
}
398+
}
399+
400+
d.name = filepath.Join(d.Dir, fileName)
401+
}
402+
403+
return d.name
404+
}
405+
455406
// DownloadChunk downloads a file chunk.
456-
func (d *Download) DownloadChunk(ctx context.Context, c Chunk, dest *os.File) error {
407+
func (d *Download) DownloadChunk(c Chunk, dest *os.File) error {
457408

458409
var (
459410
err error
460411
req *http.Request
461412
res *http.Response
462413
)
463414

464-
if req, err = NewRequest(ctx, "GET", d.URL); err != nil {
415+
if req, err = NewRequest(d.ctx, "GET", d.URL); err != nil {
465416
return err
466417
}
467418

@@ -487,8 +438,63 @@ func (d *Download) DownloadChunk(ctx context.Context, c Chunk, dest *os.File) er
487438
// NewDownload returns new *Download with context.
488439
func NewDownload(ctx context.Context, URL, dest string) *Download {
489440
return &Download{
490-
ctx: ctx,
491-
URL: URL,
492-
Dest: dest,
441+
ctx: ctx,
442+
URL: URL,
443+
Dest: dest,
444+
Client: DefaultClient,
445+
}
446+
}
447+
448+
func getDefaultConcurrency() uint {
449+
450+
c := uint(runtime.NumCPU() * 3)
451+
452+
// Set default max concurrency to 20.
453+
if c > 20 {
454+
c = 20
455+
}
456+
457+
// Set default min concurrency to 4.
458+
if c <= 2 {
459+
c = 4
460+
}
461+
462+
return c
463+
}
464+
465+
func getDefaultChunkSize(totalSize, min, max, concurrency uint64) uint64 {
466+
467+
cs := totalSize / concurrency
468+
469+
// if chunk size >= 102400000 bytes set default to (ChunkSize / 2)
470+
if cs >= 102400000 {
471+
cs = cs / 2
493472
}
473+
474+
// Set default min chunk size to 2m, or file size / 2
475+
if min == 0 {
476+
477+
min = 2097152
478+
479+
if min >= totalSize {
480+
min = totalSize / 2
481+
}
482+
}
483+
484+
// if Chunk size < Min size set chunk size to min.
485+
if cs < min {
486+
cs = min
487+
}
488+
489+
// Change ChunkSize if MaxChunkSize are set and ChunkSize > Max size
490+
if max > 0 && cs > max {
491+
cs = max
492+
}
493+
494+
// When chunk size > total file size, divide chunk / 2
495+
if cs >= totalSize {
496+
cs = totalSize / 2
497+
}
498+
499+
return cs
494500
}

0 commit comments

Comments
 (0)