Flurl
Flurl copied to clipboard
Feature Request: Download / Upload Progress Reporting
public static class Ext
{
private static async Task<bool> _DownloadFileAsync(this IFlurlClient client, string downloadDirectory,
IProgress<DownloadFileProgressInfo> progress, string downloadedFileName = null, int bufferSize = 4096)
{
bool err = false;
bool autoDispose = client.Settings.AutoDispose;
client.Settings.AutoDispose = false;
try
{
HttpResponseMessage res = await client.SendAsync(HttpMethod.Get, null, new CancellationToken?(),
HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
if (downloadedFileName == null)
if (res.Content.Headers.ContentDisposition != null)
downloadedFileName = res.Content.Headers.ContentDisposition.FileName;
else
downloadedFileName = Path.GetFileName(client.Url.Path);
if (!Directory.Exists(downloadDirectory))
Directory.CreateDirectory(downloadDirectory);
DownloadFileProgressInfo info = new DownloadFileProgressInfo();
info.FinalDataSize = res.Content.Headers.ContentLength;
ConfiguredTaskAwaitable<Stream> configuredTaskAwaitable =
res.Content.ReadAsStreamAsync().ConfigureAwait(false);
Stream httpStream = await configuredTaskAwaitable;
try
{
var assembly =
AppDomain.CurrentDomain.GetAssemblies()
.First(
s => s.FullName == "Flurl.Http, Version=1.1.1.0, Culture=neutral, PublicKeyToken=null");
Type[] types = assembly.GetTypes();
Type tFileUtil = types.First(s => s.Name == "FileUtil");
MethodInfo mFileUtil = tFileUtil.GetMethod("OpenWriteAsync",
BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Static);
Task<Stream> ts =
mFileUtil.Invoke(null, new object[] {downloadDirectory, downloadedFileName, bufferSize}) as
Task<Stream>;
configuredTaskAwaitable = ts.ConfigureAwait(false);
Stream filestream = await configuredTaskAwaitable;
try
{
Stopwatch sw = new Stopwatch();
byte[] buffer = new byte[bufferSize];
while (true)
{
sw.Reset();
sw.Start();
int num = await httpStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
sw.Stop();
if (sw.ElapsedMilliseconds > 0)
{
info.TimeTaken = sw.ElapsedMilliseconds;
info.DataSize = num;
progress.Report(info);
}
int bytesRead;
if ((bytesRead = num) != 0)
await filestream.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
else
break;
}
}
catch (Exception)
{
err = true;
}
finally
{
if (filestream != null)
{
}
filestream?.Dispose();
}
}
catch (Exception)
{
err = true;
}
finally
{
httpStream?.Dispose();
}
}
catch (Exception)
{
err = true;
}
finally
{
client.Settings.AutoDispose = autoDispose;
if (client.Settings.AutoDispose)
client.Dispose();
}
return err;
}
public static async Task<bool> DownloadFileAsync(this IFlurlClient client, string localFolderPath,
IProgress<DownloadFileProgressInfo> progress, string localFileName = null, int bufferSize = 4096)
{
return await _DownloadFileAsync(client, localFolderPath, progress, localFileName, bufferSize);
}
public static async Task<bool> DownloadFileAsync(this string url, string localFolderPath,
IProgress<DownloadFileProgressInfo> progress, string localFileName = null, int bufferSize = 4096)
{
return await new FlurlClient(url, true)._DownloadFileAsync(localFolderPath, progress, localFileName,
bufferSize);
}
public static async Task<bool> DownloadFileAsync(this Url url, string localFolderPath,
IProgress<DownloadFileProgressInfo> progress, string localFileName = null, int bufferSize = 4096)
{
return await new FlurlClient(url, true)._DownloadFileAsync(localFolderPath, progress, localFileName,
bufferSize);
}
}
public class DownloadFileProgressInfo
{
private long _dataSize;
private long _timeTaken;
public long TimeTaken
{
get { return _timeTaken; }
set
{
_timeTaken = value;
TotalTimeTaken += value;
}
}
public long TotalTimeTaken { get; private set; }
public long DataSize
{
get { return _dataSize; }
set
{
_dataSize = value;
TotalDataSize += value;
}
}
public long TotalDataSize { get; private set; }
public long? FinalDataSize { get; set; }
public long Speed
{
get
{
if (TimeTaken == 0) return 0;
return DataSize * 8 / TimeTaken;
}
}
public long TotalSpeed
{
get
{
if (TotalDataSize == 0 || TotalTimeTaken == 0) return 0;
return TotalDataSize * 8 / TotalTimeTaken;
}
}
public void Clear()
{
TimeTaken = 0;
TotalTimeTaken = 0;
DataSize = 0;
TotalDataSize = 0;
FinalDataSize = 0;
}
}
improve from http://stackoverflow.com/questions/22795399/what-is-a-valid-way-to-track-readasstreamasync-download-speed-rate
Thanks for the suggestion. I won't make any promises but I'll keep this issue open and consider progress reporting as a possible future enhancement.
As a side note (to self?) here's a simpler implementation that purports to have at least some cross-platform support: http://stackoverflow.com/questions/21169573/how-to-implement-progress-reporting-for-portable-httpclient
Any thoughts to consider this yet?
It won't make the cut for the next major version. I don't have a timeframe for it.
Would love to see this
Would like to see this as well. It it currently even possible to access the ReadAsStreamAsync()
object?
Pushed to backlog. So I guess its not part of 3.0
@Im-PJ Don't read too much in to that. Nothing has been decided about 3.0 yet, I'm just working on getting issues better organized.
I'm actively gathering feedback to help prioritize issues for 3.0. If this one is important to you, please vote for it here!
up
I was looking for something like this, I found this issue and that there was not a built-in way, so I decided to implement this helper, hopefully can help you out guys :)
public static async Task<Stream> DownloadStreamWithProgressAsync(string url, CancellationToken cancellationToken, IProgress<DownloadProgressArgs> progessReporter)
{
try
{
using(IFlurlResponse response = await url.GetAsync(cancellationToken, HttpCompletionOption.ResponseHeadersRead))
using (var stream = await response.GetStreamAsync())
{
var receivedBytes = 0;
var buffer = new byte[4096];
var totalBytes = Convert.ToDouble(response.ResponseMessage.Content.Headers.ContentLength);
var memStream = new MemoryStream();
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
await memStream.WriteAsync(buffer, 0, bytesRead);
if (bytesRead == 0)
{
break;
}
receivedBytes += bytesRead;
var args = new DownloadProgressArgs(receivedBytes, totalBytes);
progessReporter.Report(args);
}
memStream.Position = 0;
return memStream;
}
}
catch(Exception e)
{
throw e;
}
}
public class DownloadProgressArgs : EventArgs
{
public DownloadProgressArgs(int bytesReceived, double totalBytes)
{
BytesReceived = bytesReceived;
TotalBytes = totalBytes;
}
public double TotalBytes { get; }
public double BytesReceived { get; }
public double PercentComplete => 100 * (BytesReceived / TotalBytes);
}