@@ -87,6 +87,12 @@ func (c *S3Client) Head(ctx context.Context, key string) (bool, *s3.HeadObjectOu
87
87
return true , output , nil
88
88
}
89
89
90
+ type chunkResult struct {
91
+ index int
92
+ data []byte
93
+ err error
94
+ }
95
+
90
96
func (c * S3Client ) DownloadIntoBuffer (ctx context.Context , key string , buffer * bytes.Buffer ) error {
91
97
ok , head , err := c .Head (ctx , key )
92
98
if err != nil || ! ok {
@@ -98,26 +104,19 @@ func (c *S3Client) DownloadIntoBuffer(ctx context.Context, key string, buffer *b
98
104
}
99
105
100
106
numChunks := int ((size + c .DownloadChunkSize - 1 ) / c .DownloadChunkSize )
101
- chunks := make ([][] byte , numChunks )
107
+ chunkCh := make (chan chunkResult , numChunks )
102
108
sem := make (chan struct {}, c .DownloadConcurrency )
103
- var wg sync.WaitGroup
104
109
105
110
ctx , cancel := context .WithCancel (ctx )
106
111
defer cancel ()
107
112
108
- errCh := make (chan error , 1 )
109
-
113
+ var wg sync.WaitGroup
110
114
for i := 0 ; i < numChunks ; i ++ {
111
115
wg .Add (1 )
112
-
113
116
go func (i int ) {
117
+ defer wg .Done ()
114
118
sem <- struct {}{}
115
119
defer func () { <- sem }()
116
- defer wg .Done ()
117
-
118
- if ctx .Err () != nil {
119
- return
120
- }
121
120
122
121
start := int64 (i ) * c .DownloadChunkSize
123
122
end := start + c .DownloadChunkSize - 1
@@ -132,41 +131,43 @@ func (c *S3Client) DownloadIntoBuffer(ctx context.Context, key string, buffer *b
132
131
Range : & rangeHeader ,
133
132
})
134
133
if err != nil {
135
- select {
136
- case errCh <- fmt .Errorf ("range request failed for %s: %w" , rangeHeader , err ):
137
- cancel ()
138
- default :
139
- }
134
+ chunkCh <- chunkResult {i , nil , fmt .Errorf ("range request failed for %s: %w" , rangeHeader , err )}
135
+ cancel ()
140
136
return
141
137
}
142
138
defer resp .Body .Close ()
143
139
144
140
part := make ([]byte , end - start + 1 )
145
141
n , err := io .ReadFull (resp .Body , part )
146
142
if err != nil && err != io .EOF && err != io .ErrUnexpectedEOF {
147
- select {
148
- case errCh <- fmt .Errorf ("error reading range %s: %w" , rangeHeader , err ):
149
- cancel ()
150
- default :
151
- }
143
+ chunkCh <- chunkResult {i , nil , fmt .Errorf ("error reading range %s: %w" , rangeHeader , err )}
144
+ cancel ()
152
145
return
153
146
}
154
-
155
- chunks [i ] = part [:n ]
147
+ chunkCh <- chunkResult {i , part [:n ], nil }
156
148
}(i )
157
149
}
158
150
159
- wg .Wait ()
160
- close (errCh )
151
+ go func () {
152
+ wg .Wait ()
153
+ close (chunkCh )
154
+ }()
161
155
162
- if err , ok := <- errCh ; ok {
163
- return err
156
+ chunks := make ([][]byte , numChunks )
157
+ var errs []error
158
+ for res := range chunkCh {
159
+ if res .err != nil {
160
+ errs = append (errs , res .err )
161
+ }
162
+ chunks [res .index ] = res .data
163
+ }
164
+ if len (errs ) > 0 {
165
+ return fmt .Errorf ("download errors: %v" , errs )
164
166
}
165
167
166
168
buffer .Reset ()
167
169
for _ , chunk := range chunks {
168
170
buffer .Write (chunk )
169
171
}
170
-
171
172
return nil
172
173
}
0 commit comments