C# 异步Base64下载流到文件
我正在尝试开发一个.NET应用程序,它将与一个.NET核心服务器应用程序(我没有开发)通信。主要目标是下载一个文件。因为客户端应用程序将有一个WPF gui,所以整个下载应该异步进行 通过阅读服务器应用程序的API,我知道请求的响应是一个包含文件内容的Base64编码字符串 我想做的是异步发送一个请求,获取它的响应流,从该流异步读取一个字符,base64解码,异步写入文件(见下面的代码) 但是Convert.FromBase64CharArray通常会出现异常而失败 base-64字符数组或字符串的长度无效 有时,此操作会成功,但下载过早结束(downloadedLengthC# 异步Base64下载流到文件,c#,asynchronous,stream,webclient-download,C#,Asynchronous,Stream,Webclient Download,我正在尝试开发一个.NET应用程序,它将与一个.NET核心服务器应用程序(我没有开发)通信。主要目标是下载一个文件。因为客户端应用程序将有一个WPF gui,所以整个下载应该异步进行 通过阅读服务器应用程序的API,我知道请求的响应是一个包含文件内容的Base64编码字符串 我想做的是异步发送一个请求,获取它的响应流,从该流异步读取一个字符,base64解码,异步写入文件(见下面的代码) 但是Convert.FromBase64CharArray通常会出现异常而失败 base-64字符数组或字符
- 使用streamReader.ReadToEndAsync()对完整字符串进行解码,async write:worked,但下载115MB时使用了大约600MB的RAM
- 使{read,decode,write}块异步,而不是异步读取,解码,异步写入:无改进
- 完全没有异步:有时会失败,但不像异步版本那样频繁
- 使用FromBase64Transform::TransformBlock而不是Convert。FromBase64CharArray:未在合理的时间内完成下载,因为inputBlockSize设置为fix 1字节(下载约115MB)
- 通过SSH隧道进行通信以忽略Apache服务器:下载甚至没有启动
- 让客户机和服务器在同一台机器上运行:似乎工作正常
- 客户端:Windows 7 x64、.NET 4.6.1
- 服务器:Ubuntu 16.04、Apache 2.4、.NET Core 2.1.4
private async Task<WebResponse> DoGetRequestAsync(string requestString)
{
var completeRequestUrl = $"{_options.StoreUrl}/api/{requestString}";
try
{
RequestStarted?.Invoke(true);
var request = (HttpWebRequest)WebRequest.Create(completeRequestUrl);
request.ContentType = "text/plain";
request.Method = "GET";
var response = await request.GetResponseAsync();
RequestFinished?.Invoke(true);
return response;
}
catch (Exception e)
{
Console.WriteLine($"ERROR: {e.Message}");
}
return null;
}
专用异步任务DoGetRequestAsync(字符串请求字符串)
{
var completeRequestUrl=$“{u options.StoreUrl}/api/{requestString}”;
尝试
{
RequestStarted?.Invoke(true);
var-request=(HttpWebRequest)WebRequest.Create(completeRequestUrl);
request.ContentType=“text/plain”;
request.Method=“GET”;
var response=wait request.GetResponseAsync();
RequestFinished?.Invoke(true);
返回响应;
}
捕获(例外e)
{
WriteLine($“错误:{e.Message}”);
}
返回null;
}
处理响应的函数:
public async Task<string> DownloadPackage(string vendor, string package)
{
// declaring some vars
using (var response = await DoGetRequestAsync(requestString))
{
var totalLength = response.ContentLength;
var downloadedLength = 0;
var charBuffer = new char[4 * 1024];
try
{
using (var stream = response.GetResponseStream())
{
if (stream != null)
{
using (var reader = new StreamReader(stream))
using (var fStream = File.Create(filename))
{
while (!reader.EndOfStream)
{
var readBytes = await reader.ReadAsync(charBuffer, 0, charBuffer.Length);
var decoded = Convert.FromBase64CharArray(charBuffer, 0, readBytes);
await fStream.WriteAsync(decoded, 0, decoded.Length);
downloadedLength += readBytes;
DownloadProgress?.Invoke((float)downloadedLength / totalLength * 100.0f);
}
}
}
}
if (downloadedLength < totalLength)
{
throw new Exception($"Download failed due to a network error. Downloaded {downloadedLength} Bytes.");
}
// some follow-up stuff
return filename;
}
catch (Exception e)
{
Console.WriteLine("Error!");
Console.WriteLine(e.Message);
throw;
}
}
}
公共异步任务下载包(字符串供应商,字符串包)
{
//申报一些VAR
使用(var响应=等待DoGetRequestAsync(请求字符串))
{
var totalLength=响应.ContentLength;
var downloadedLength=0;
var charBuffer=新字符[4*1024];
尝试
{
使用(var stream=response.GetResponseStream())
{
if(流!=null)
{
使用(变量读取器=新的流读取器(流))
使用(var fStream=File.Create(文件名))
{
而(!reader.EndOfStream)
{
var readBytes=await reader.ReadAsync(charBuffer,0,charBuffer.Length);
var decoded=Convert.FromBase64CharArray(charBuffer,0,readBytes);
等待fStream.WriteAsync(已解码,0,已解码.长度);
downloadedLength+=readBytes;
下载进度?.Invoke((浮动)下载长度/总长度*100.0f);
}
}
}
}
if(下载长度<总长度)
{
抛出新异常($“由于网络错误,下载失败。已下载{downloadedLength}字节。”);
}
//一些后续的东西
返回文件名;
}
捕获(例外e)
{
控制台。WriteLine(“错误!”);
控制台写入线(e.Message);
投掷;
}
}
}
你知道是什么导致了这个错误吗
编辑:
好的,我试着实施费尔多提出的解决方案。因为我没有删除辅助缓冲区的解码内容,所以现在需要更多内存来执行下载。但是我可以省略StreamReader,直接从流中读取。这导致了另一个例外:
无法从传输连接读取数据:连接已关闭
无论是同步还是同步。似乎是我第一次怀疑的证据。但我仍然不知道如何解决这个问题。与问题无关:我不会将任务异步和事件混合使用。要报告进度,请使用其他工具(如curl)尝试下载是否一致成功?也许是你的网络在某个地方有一些脆弱的连接?服务器在下载或类似的东西时也不会达到100%的CPU吗?@Fildor:只是尝试了wget for Windows。下载成功了好几次。服务器显示大约12%的CPU使用率。网络和服务器似乎是一个有趣的想法。我要试试看。advanceAn API中仅支持将文件生成为base 64编码字符串的Thx被破坏。与开发人员讨论支持本机二进制下载的问题,这对你们两人来说都应该是比较少的工作。