Skip to content

Commit a7b83b8

Browse files
committed
TD-5348 azure file download service
1 parent 877ddbd commit a7b83b8

File tree

3 files changed

+37
-56
lines changed

3 files changed

+37
-56
lines changed

LearningHub.Nhs.WebUI/Controllers/Api/ResourceController.cs

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,10 @@ public async Task<IActionResult> DownloadResource(string filePath, string fileNa
7474
}
7575

7676
var file = await this.fileService.DownloadFileAsync(filePath, fileName);
77+
7778
if (file != null)
7879
{
79-
// Set response headers.
80-
this.Response.ContentType = file.ContentType;
81-
this.Response.ContentLength = file.ContentLength;
82-
var contentDisposition = new ContentDispositionHeaderValue("attachment") { FileNameStar = fileName };
83-
this.Response.Headers["Content-Disposition"] = contentDisposition.ToString();
84-
85-
// Stream the file in chunks with periodic flushes to keep the connection active.
86-
await this.StreamFileWithKeepAliveAsync(file.Content, this.Response.Body, this.HttpContext.RequestAborted);
87-
return this.Ok();
80+
return !string.IsNullOrEmpty(file.DownloadUrl) ? this.Redirect(file.DownloadUrl) : this.File(file.Content, file.ContentType, fileName);
8881
}
8982
else
9083
{
@@ -120,15 +113,7 @@ public async Task<IActionResult> DownloadResourceAndRecordActivity(int resourceV
120113
};
121114
await this.activityService.CreateResourceActivityAsync(activity);
122115

123-
// Set response headers.
124-
this.Response.ContentType = file.ContentType;
125-
this.Response.ContentLength = file.ContentLength;
126-
var contentDisposition = new ContentDispositionHeaderValue("attachment") { FileNameStar = fileName };
127-
this.Response.Headers["Content-Disposition"] = contentDisposition.ToString();
128-
129-
// Stream the file in chunks with periodic flushes to keep the connection active.
130-
await this.StreamFileWithKeepAliveAsync(file.Content, this.Response.Body, this.HttpContext.RequestAborted);
131-
return this.Ok();
116+
return !string.IsNullOrEmpty(file.DownloadUrl) ? this.Redirect(file.DownloadUrl) : this.File(file.Content, file.ContentType, fileName);
132117
}
133118
else
134119
{
@@ -607,20 +592,5 @@ public async Task<List<string>> GetObsoleteResourceFile(int resourceVersionId, b
607592
var result = await this.resourceService.GetObsoleteResourceFile(resourceVersionId, deletedResource);
608593
return result;
609594
}
610-
611-
/// <summary>
612-
/// Reads from the source stream in chunks and writes to the destination stream,
613-
/// flushing after each chunk to help keep the connection active.
614-
/// </summary>
615-
private async Task StreamFileWithKeepAliveAsync(Stream source, Stream destination, CancellationToken cancellationToken)
616-
{
617-
byte[] buffer = new byte[8192];
618-
int bytesRead;
619-
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
620-
{
621-
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken);
622-
await destination.FlushAsync(cancellationToken);
623-
}
624-
}
625595
}
626596
}

LearningHub.Nhs.WebUI/Models/FileDownloadResponse.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,10 @@ public class FileDownloadResponse
2121
/// Gets or sets the ContentType.
2222
/// </summary>
2323
public long ContentLength { get; set; }
24+
25+
/// <summary>
26+
/// Gets or sets when downloading large files, a SAS URL is returned so the client can download directly from Azure Files.
27+
/// </summary>
28+
public string DownloadUrl { get; set; }
2429
}
2530
}

LearningHub.Nhs.WebUI/Services/FileService.cs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
namespace LearningHub.Nhs.WebUI.Services
22
{
33
using System;
4-
using System.Buffers;
54
using System.Collections.Generic;
65
using System.IO;
76
using System.Linq;
8-
using System.Threading;
97
using System.Threading.Tasks;
10-
using System.Threading.Tasks.Dataflow;
11-
using Azure;
12-
using Azure.Storage;
138
using Azure.Storage.Files.Shares;
149
using Azure.Storage.Files.Shares.Models;
10+
using Azure.Storage.Sas;
1511
using LearningHub.Nhs.Models.Resource;
1612
using LearningHub.Nhs.WebUI.Configuration;
1713
using LearningHub.Nhs.WebUI.Interfaces;
@@ -145,27 +141,19 @@ public async Task<FileDownloadResponse> DownloadFileAsync(string filePath, strin
145141

146142
try
147143
{
148-
if (fileSize <= 900 * 1024 * 1024)
144+
var response = new FileDownloadResponse
149145
{
150-
// For smaller files, download the entire file as a stream.
151-
var response = await file.DownloadAsync();
152-
return new FileDownloadResponse
153-
{
154-
Content = response.Value.Content,
155-
ContentType = properties.Value.ContentType,
156-
ContentLength = fileSize,
157-
};
158-
}
159-
else
146+
ContentType = properties.Value.ContentType,
147+
ContentLength = fileSize,
148+
Content = await file.OpenReadAsync(),
149+
};
150+
151+
if (fileSize >= 999 * 1024 * 1024)
160152
{
161-
// For large files, open a read stream
162-
return new FileDownloadResponse
163-
{
164-
Content = await file.OpenReadAsync(),
165-
ContentType = properties.Value.ContentType,
166-
ContentLength = fileSize,
167-
};
153+
response.DownloadUrl = this.GenerateSasUriForFile(file);
168154
}
155+
156+
return response;
169157
}
170158
catch (Exception ex)
171159
{
@@ -461,5 +449,23 @@ private async Task<ShareFileClient> FindFileAsync(string filePath, string fileNa
461449

462450
return null;
463451
}
452+
453+
private string GenerateSasUriForFile(ShareFileClient fileClient)
454+
{
455+
if (fileClient.CanGenerateSasUri)
456+
{
457+
ShareSasBuilder sasBuilder = new ShareSasBuilder(ShareFileSasPermissions.Read, DateTimeOffset.UtcNow.AddMinutes(20))
458+
{
459+
Protocol = SasProtocol.Https,
460+
};
461+
462+
Uri sasUri = fileClient.GenerateSasUri(sasBuilder);
463+
return sasUri.ToString();
464+
}
465+
else
466+
{
467+
throw new InvalidOperationException("Unable to generate SAS URI for the file.");
468+
}
469+
}
464470
}
465471
}

0 commit comments

Comments
 (0)