/// <summary> /// Download a file with an <see cref="ApiRequest"/> /// </summary> /// <remarks> /// Bypasses <see cref="ValidateAndProcess{T}"/> /// </remarks> public async Task PerformAsync(ApiFileRequest request, Action <long, long?> progressUpdated = null, CancellationToken token = default) { // check request data is valid ValidateRequest(request); if (string.IsNullOrWhiteSpace(request.Destination)) { throw new NullRequestException(); } // get raw response var response = await InternalPerform(request.Build(this), Task.FromResult, false, token).ConfigureAwait(false); // validate response.EnsureSuccessStatusCode(); // create a new filestream and copy all data into using var stream = File.Open(request.Destination, request.FileCreationMode); using var networkStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); var buffer = ArrayPool <byte> .Shared.Rent(4096); try { int count; var iterations = 0; while ((count = await networkStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) > 0) { Interlocked.Increment(ref iterations); await stream.WriteAsync(buffer, 0, count, token).ConfigureAwait(false); // check every 10th time to stop bottlenecks (use CompareExchange to stop the int from overflowing from insanely large file downloads) if (Interlocked.CompareExchange(ref iterations, 0, 100) == 100) { progressUpdated?.Invoke(stream.Length, response.Content.Headers.ContentLength); } } // flush, return buffer and send a final update await stream.FlushAsync(token).ConfigureAwait(false); } finally { ArrayPool <byte> .Shared.Return(buffer); } progressUpdated?.Invoke(stream.Length, response.Content.Headers.ContentLength); }
/// <summary> /// Download a file with an <see cref="ApiRequest"/> /// </summary> /// <remarks> /// Bypasses <see cref="ValidateAndProcess{T}"/> /// </remarks> public void Perform(ApiFileRequest request, Action <long, long?> progressUpdated = null, CancellationToken token = default) { PerformAsync(request, progressUpdated, token).Wait(token); }