11namespace LearningHub . Nhs . WebUI . Services
22{
33 using System ;
4+ using System . Buffers ;
45 using System . Collections . Generic ;
56 using System . IO ;
67 using System . Linq ;
8+ using System . Threading ;
79 using System . Threading . Tasks ;
10+ using System . Threading . Tasks . Dataflow ;
11+ using Azure ;
812 using Azure . Storage . Files . Shares ;
913 using Azure . Storage . Files . Shares . Models ;
1014 using LearningHub . Nhs . Models . Resource ;
1115 using LearningHub . Nhs . WebUI . Configuration ;
1216 using LearningHub . Nhs . WebUI . Interfaces ;
17+ using LearningHub . Nhs . WebUI . Models ;
1318 using Microsoft . AspNetCore . StaticFiles ;
1419 using Microsoft . Extensions . Options ;
1520
@@ -125,32 +130,79 @@ public async Task DeleteChunkDirectory(string directoryRef, int chunks)
125130 /// </summary>
126131 /// <param name="filePath">The filePath.</param>
127132 /// <param name="fileName">The fileName.</param>
128- /// <returns>The <see cref="Task{CloudFile }"/>.</returns>
129- public async Task < ShareFileDownloadInfo > DownloadFileAsync ( string filePath , string fileName )
133+ /// <returns>The <see cref="Task{FileDownloadResponse }"/>.</returns>
134+ public async Task < FileDownloadResponse > DownloadFileAsync ( string filePath , string fileName )
130135 {
131- var directory = this . ShareClient . GetDirectoryClient ( filePath ) ;
132- var sourceDirectory = this . InputArchiveShareClient . GetDirectoryClient ( filePath ) ;
136+ const int ChunkSizeInMB = 100 ;
137+ const int MaxParallelDownloads = 8 ;
138+ int chunkSize = ChunkSizeInMB * 1024 * 1024 ;
133139
134- if ( await directory . ExistsAsync ( ) )
140+ var file = await this . FindFileAsync ( filePath , fileName ) ;
141+ if ( file == null )
135142 {
136- var file = directory . GetFileClient ( fileName ) ;
143+ return null ;
144+ }
137145
138- if ( await file . ExistsAsync ( ) )
146+ var properties = await file . GetPropertiesAsync ( ) ;
147+ long fileSize = properties . Value . ContentLength ;
148+
149+ // If the file size is less than 500 MB, download it directly
150+ if ( fileSize <= 500 * 1024 * 1024 )
151+ {
152+ var response = await file . DownloadAsync ( ) ;
153+ return new FileDownloadResponse
139154 {
140- return await file . DownloadAsync ( ) ;
141- }
155+ Content = response . Value . Content ,
156+ ContentType = properties . Value . ContentType ,
157+ ContentLength = fileSize ,
158+ } ;
142159 }
143- else if ( await sourceDirectory . ExistsAsync ( ) )
144- {
145- var file = sourceDirectory . GetFileClient ( fileName ) ;
146160
147- if ( await file . ExistsAsync ( ) )
161+ var pipe = new System . IO . Pipelines . Pipe ( ) ;
162+ var semaphore = new SemaphoreSlim ( 1 , 1 ) ;
163+
164+ var downloadBlock = new ActionBlock < long > (
165+ async offset =>
148166 {
149- return await file . DownloadAsync ( ) ;
150- }
167+ long rangeSize = Math . Min ( chunkSize , fileSize - offset ) ;
168+ try
169+ {
170+ var response = await file . DownloadAsync ( new HttpRange ( offset , rangeSize ) ) ;
171+ var buffer = new byte [ rangeSize ] ;
172+ await response . Value . Content . ReadAsync ( buffer , 0 , ( int ) rangeSize ) ;
173+
174+ await semaphore . WaitAsync ( ) ;
175+ try
176+ {
177+ pipe . Writer . Write ( buffer ) ;
178+ }
179+ finally
180+ {
181+ semaphore . Release ( ) ;
182+ }
183+ }
184+ catch ( Exception ex )
185+ {
186+ throw new Exception ( $ "Error downloading chunk at offset { offset } : { ex . Message } ") ;
187+ }
188+ } ,
189+ new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = MaxParallelDownloads , EnsureOrdered = false } ) ;
190+
191+ for ( long offset = 0 ; offset < fileSize ; offset += chunkSize )
192+ {
193+ downloadBlock . Post ( offset ) ;
151194 }
152195
153- return null ;
196+ downloadBlock . Complete ( ) ;
197+ await downloadBlock . Completion ;
198+ await pipe . Writer . CompleteAsync ( ) ;
199+
200+ return new FileDownloadResponse
201+ {
202+ Content = pipe . Reader . AsStream ( ) ,
203+ ContentType = properties . Value . ContentType ,
204+ ContentLength = fileSize ,
205+ } ;
154206 }
155207
156208 /// <summary>
@@ -418,5 +470,28 @@ private async Task MoveInPutDirectoryToArchive(List<string> allDirectoryRef)
418470 }
419471 }
420472 }
473+
474+ private async Task < ShareFileClient > FindFileAsync ( string filePath , string fileName )
475+ {
476+ var directories = new [ ]
477+ {
478+ this . ShareClient . GetDirectoryClient ( filePath ) ,
479+ this . InputArchiveShareClient . GetDirectoryClient ( filePath ) ,
480+ } ;
481+
482+ foreach ( var directory in directories )
483+ {
484+ if ( await directory . ExistsAsync ( ) )
485+ {
486+ var file = directory . GetFileClient ( fileName ) ;
487+ if ( await file . ExistsAsync ( ) )
488+ {
489+ return file ;
490+ }
491+ }
492+ }
493+
494+ return null ;
495+ }
421496 }
422497}
0 commit comments