C# RestSharp 库 - 加载大型 "application/octect-stream" 时报告进度
C# RestSharp Library - Report Progress when loading a large "application/octect-stream"
我知道这个问题已提交多次,但最后的答案已过时,我想知道今天是否有可用的解决方案?这不是功能请求。我宁愿寻找任何可行的解决方法。
我在与我的 API 通信的客户端上使用 RestSharp。 API 以 "application/octect-stream" 回复,下载可能需要几分钟时间。 Link 到 GitHub code here.
public void Download()
{
if (NewerApp == null) return;
var client = new RestClient(ServerAddress);
var request = new RestRequest("/Apps/", Method.POST);
request.AddParameter("id", CurrentApp.EncryptedId());
request.AddParameter("action", "download");
var asyncHandle = client.ExecuteAsync<App>(request, response => {
HandleResponseToDownloadRequest(response);
});
}
我需要向我的 UI 报告 "response" 的接收进度,以构建进度条或类似的东西。我已经知道要接收的预期数据量(通过之前的 API 响应),我只需要知道在接收和解析完整响应之前接收了多少字节。
我认为 RestSharp 目前不提供 'report progress' 类型的事件,对吧?我可以看到几种方法:
- 使用client.DownloadData(请求).SaveAs(路径);。可能文件是在下载时创建的。我可以读取文件的大小来报告下载进度。但我的第一印象是客户端先下载数据,然后保存文件。那样的话,那也无济于事了。
- 使用流加载响应。定期或每次缓冲区大小扩展时评估流的大小。
- 将响应类型从 API 更改为另一种(例如,在 JSON 中发送数据?)。
- 还有其他选择吗?
你怎么看?有人设法报告上传进度吗?
我可以在 ExecuteAsync 仍在执行时访问 'response' 的 RawBytes 吗?我应该改用 Execute(不使用 Asyn)吗?我应该在定期更新时使用流和更新观察流的大小吗?
我设法获得了进度报告,但没有使用 RestSharp。我在kievic's answer here的基础上使用了System.Net.HttpClient。正如 kievic 强调的那样,这里的关键是 aClient.SendAsync returns 一旦 HTTP headers 被接收和读取,但内容仍在加载。然后内容通过 while 循环中的流缓慢加载。 RestSharp 似乎没有启用此功能,或者我无法实现。
public async Task DownloadAsync(Action<bool> callback)
{
if (NewerApp == null) return;
// Your original code.
HttpClientHandler aHandler = new HttpClientHandler();
aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
HttpClient aClient = new HttpClient(aHandler);
aClient.DefaultRequestHeaders.ExpectContinue = false;
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, ServerAddress + "/Apps/");
string content = "id=" + CurrentApp.EncryptedId() + "&action=download";
message.Content = new StringContent(content);
HttpResponseMessage response = await aClient.SendAsync(message,
HttpCompletionOption.ResponseHeadersRead); // Important! ResponseHeadersRead.
// New code.
Stream stream = await response.Content.ReadAsStreamAsync();
MemoryStream memStream = new MemoryStream();
// Start reading the stream
var res = stream.CopyToAsync(memStream);
// While reading the stream
while (true)
{
// Report progress
this.DownloadedSize = memStream.Length;
this.Progress = 100.0 * (double)memStream.Length / (double)NewerApp.Filesize;
// Leave if no new data was read
if (res.IsCompleted)
{
// Report progress one last time
this.DownloadedSize = memStream.Length;
this.Progress = 100.0 * (double)memStream.Length / (double)NewerApp.Filesize;
break;
}
}
// Get the bytes from the memory stream
byte[] responseContent = new byte[memStream.Length];
memStream.Position = 0;
memStream.Read(responseContent, 0, responseContent.Length);
// Function has ended - return whether the app was donwloaded
// properly and verified, or not
callback(HandleResponseToDownloadRequest(responseContent));
}
每次 this.DownloadedSize 和 this.Progress 被分配一个新值时,它们都会触发一个事件,该事件可以被 UI.
抓住
private double progress = 0;
/// <summary>
/// Progress of the download of the App. From 0.0 (%) to 100.0 (%)
/// </summary>
public double Progress
{
get { return progress; }
set
{
// Max / Min
double val = value;
if (val > 100.0) val = 100;
else if (val < 0.0) val = 0.0;
// Assign value
if (progress != val)
{
progress = val;
OnProgressReport("Progress");
OnPropertyChanged("Progress");
}
}
}
public long downloadedSize = 0;
/// <summary>
/// Quantity of bytes downloaded of the app.
/// Note: there can be more bytes downloaded than advertized because
/// the quantity of advertize filesize is not encrypted while the
/// received bytes are encrypted.
/// TODO: advertize the size of the encrypted file.
/// </summary>
public long DownloadedSize
{
get
{
return downloadedSize;
}
set
{
if (downloadedSize != value)
{
downloadedSize = value;
OnDownloadedSizeReport("DownloadedSize");
}
}
}
/// <summary>
/// Fired when the progress of the download of the app file
/// has updated (more bytes received).
/// </summary>
public event PropertyChangedEventHandler ProgressReport;
protected void OnProgressReport(string name)
{
PropertyChangedEventHandler handler = ProgressReport;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// Fired when the progress of the download of the app file
/// has updated (more bytes received).
/// </summary>
public event PropertyChangedEventHandler DownloadedSizeReport;
protected void OnDownloadedSizeReport(string name)
{
PropertyChangedEventHandler handler = DownloadedSizeReport;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
我这样调用 DownloadAsync:
// Start the download - await makes the execution of the method in background
// so that UI can refresh and keep responsive.
// downloaded: bool, true if file properly downloaded
// Updater_AppDownloaded: function called once download has ended (failed or succeeded, either way)
await System.Threading.Tasks.Task.Run(() =>
Updater.DownloadAsync(downloaded =>
Updater_AppDownloaded(downloaded)));
我知道这个问题已提交多次,但最后的答案已过时,我想知道今天是否有可用的解决方案?这不是功能请求。我宁愿寻找任何可行的解决方法。
我在与我的 API 通信的客户端上使用 RestSharp。 API 以 "application/octect-stream" 回复,下载可能需要几分钟时间。 Link 到 GitHub code here.
public void Download()
{
if (NewerApp == null) return;
var client = new RestClient(ServerAddress);
var request = new RestRequest("/Apps/", Method.POST);
request.AddParameter("id", CurrentApp.EncryptedId());
request.AddParameter("action", "download");
var asyncHandle = client.ExecuteAsync<App>(request, response => {
HandleResponseToDownloadRequest(response);
});
}
我需要向我的 UI 报告 "response" 的接收进度,以构建进度条或类似的东西。我已经知道要接收的预期数据量(通过之前的 API 响应),我只需要知道在接收和解析完整响应之前接收了多少字节。
我认为 RestSharp 目前不提供 'report progress' 类型的事件,对吧?我可以看到几种方法:
- 使用client.DownloadData(请求).SaveAs(路径);。可能文件是在下载时创建的。我可以读取文件的大小来报告下载进度。但我的第一印象是客户端先下载数据,然后保存文件。那样的话,那也无济于事了。
- 使用流加载响应。定期或每次缓冲区大小扩展时评估流的大小。
- 将响应类型从 API 更改为另一种(例如,在 JSON 中发送数据?)。
- 还有其他选择吗?
你怎么看?有人设法报告上传进度吗?
我可以在 ExecuteAsync 仍在执行时访问 'response' 的 RawBytes 吗?我应该改用 Execute(不使用 Asyn)吗?我应该在定期更新时使用流和更新观察流的大小吗?
我设法获得了进度报告,但没有使用 RestSharp。我在kievic's answer here的基础上使用了System.Net.HttpClient。正如 kievic 强调的那样,这里的关键是 aClient.SendAsync returns 一旦 HTTP headers 被接收和读取,但内容仍在加载。然后内容通过 while 循环中的流缓慢加载。 RestSharp 似乎没有启用此功能,或者我无法实现。
public async Task DownloadAsync(Action<bool> callback)
{
if (NewerApp == null) return;
// Your original code.
HttpClientHandler aHandler = new HttpClientHandler();
aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
HttpClient aClient = new HttpClient(aHandler);
aClient.DefaultRequestHeaders.ExpectContinue = false;
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, ServerAddress + "/Apps/");
string content = "id=" + CurrentApp.EncryptedId() + "&action=download";
message.Content = new StringContent(content);
HttpResponseMessage response = await aClient.SendAsync(message,
HttpCompletionOption.ResponseHeadersRead); // Important! ResponseHeadersRead.
// New code.
Stream stream = await response.Content.ReadAsStreamAsync();
MemoryStream memStream = new MemoryStream();
// Start reading the stream
var res = stream.CopyToAsync(memStream);
// While reading the stream
while (true)
{
// Report progress
this.DownloadedSize = memStream.Length;
this.Progress = 100.0 * (double)memStream.Length / (double)NewerApp.Filesize;
// Leave if no new data was read
if (res.IsCompleted)
{
// Report progress one last time
this.DownloadedSize = memStream.Length;
this.Progress = 100.0 * (double)memStream.Length / (double)NewerApp.Filesize;
break;
}
}
// Get the bytes from the memory stream
byte[] responseContent = new byte[memStream.Length];
memStream.Position = 0;
memStream.Read(responseContent, 0, responseContent.Length);
// Function has ended - return whether the app was donwloaded
// properly and verified, or not
callback(HandleResponseToDownloadRequest(responseContent));
}
每次 this.DownloadedSize 和 this.Progress 被分配一个新值时,它们都会触发一个事件,该事件可以被 UI.
抓住 private double progress = 0;
/// <summary>
/// Progress of the download of the App. From 0.0 (%) to 100.0 (%)
/// </summary>
public double Progress
{
get { return progress; }
set
{
// Max / Min
double val = value;
if (val > 100.0) val = 100;
else if (val < 0.0) val = 0.0;
// Assign value
if (progress != val)
{
progress = val;
OnProgressReport("Progress");
OnPropertyChanged("Progress");
}
}
}
public long downloadedSize = 0;
/// <summary>
/// Quantity of bytes downloaded of the app.
/// Note: there can be more bytes downloaded than advertized because
/// the quantity of advertize filesize is not encrypted while the
/// received bytes are encrypted.
/// TODO: advertize the size of the encrypted file.
/// </summary>
public long DownloadedSize
{
get
{
return downloadedSize;
}
set
{
if (downloadedSize != value)
{
downloadedSize = value;
OnDownloadedSizeReport("DownloadedSize");
}
}
}
/// <summary>
/// Fired when the progress of the download of the app file
/// has updated (more bytes received).
/// </summary>
public event PropertyChangedEventHandler ProgressReport;
protected void OnProgressReport(string name)
{
PropertyChangedEventHandler handler = ProgressReport;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// Fired when the progress of the download of the app file
/// has updated (more bytes received).
/// </summary>
public event PropertyChangedEventHandler DownloadedSizeReport;
protected void OnDownloadedSizeReport(string name)
{
PropertyChangedEventHandler handler = DownloadedSizeReport;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
我这样调用 DownloadAsync:
// Start the download - await makes the execution of the method in background
// so that UI can refresh and keep responsive.
// downloaded: bool, true if file properly downloaded
// Updater_AppDownloaded: function called once download has ended (failed or succeeded, either way)
await System.Threading.Tasks.Task.Run(() =>
Updater.DownloadAsync(downloaded =>
Updater_AppDownloaded(downloaded)));