I have an API that I’m connecting to when downloading a file from our website (Angular). The user clicks the link and the website shows the download progress then the browser shows the Downloads window once complete. All this works well until the file being downloaded is larger than 2gb. From my research, this is a limitation to File.ReadAllBytes()
. I have looked at many articles including:
Download file with WebClient or HttpClient?
Efficiently Streaming Large HTTP Responses With HttpClient
And come to the conclusion that large files need to be handled in chunks. Here’s my attempt at doing such:
public async Task<IHttpActionResult> Get(string data)
{
int bufferSize = 8192;
string fileName = "productABC_documentation.zip"; //~119MB
string tempFile = $@"C:NoSyncDatafilesproductABC{fileName}";
string url = $"https://localhost:44347/asc/customers/public/files/productABC/{fileName}";
try
{
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode();
using (Stream stream = await response.Content.ReadAsStreamAsync(),
fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, true))
{
int bytesRead;
byte[] buffer = new byte[bufferSize];
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
await fileStream.WriteAsync(buffer, 0, bytesRead);
return new FileDownloadResult(stream, Request, fileName);
}
}
}
catch (Exception e)
{
//Throw error on any exception.
string message = Functions.BuildErrorMessage("There was an error downloading the file.", e);
throw new Exception("There was an error downloading the file." + Environment.NewLine + message);
}
finally
{
}
}
Here is the FileDownloadResult class:
public class FileDownloadResult : IHttpActionResult
{
string fileName;
Stream fileStream;
HttpRequestMessage httpRequestMessage;
HttpResponseMessage httpResponseMessage;
public FileDownloadResult(Stream data, HttpRequestMessage request, string filename)
{
fileStream = data;
httpRequestMessage = request;
fileName = filename;
}
public Task<HttpResponseMessage> ExecuteAsync(System.Threading.CancellationToken cancellationToken)
{
httpResponseMessage = httpRequestMessage.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new StreamContent(fileStream);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName;
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
return Task.FromResult(httpResponseMessage);
}
}
When I run the API and website locally, I can click the file link and I can watch it download into the tempFile
location C:NoSyncDatafilesproductABC
completely but the browser doesn’t open the Downloads window and the file does not appear in the Downloads folder. I’m assuming I can only write to the tempFile
location because it’s running locally. I don’t think an API can specify where to place a file on the client (I could be wrong but I’m not aware that it can). How can I make it so when I click the link, the browser shows it being downloaded into the Downloads folder?