我的 C# 应用程序将文件上传到某个 API,我使用的是多部分请求,即我正在上传文件的 json 字符串和二进制内容,它适用于大多数文件,但对于极少数文件,它给出了例外,我的意思是让我们尝试对于名为50MB.zip
我的文件,出现异常:
A task was canceled. ::: ::: System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
Run Code Online (Sandbox Code Playgroud)
我的代码大致如下:
public async Task<Dictionary<string , string>> Upload(string filePath)
{
FileInfo fi = new FileInfo(FilePath);
string jsonString="some json string";
byte[] fileContents=File.ReadAllBytes(fi.FullName);
Uri webService = new Uri(url);
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post , webService);
requestMessage.Method = HttpMethod.Post;
requestMessage.Headers.Add("Authorization" , "MyKey1234");
const string boundry = "------------------My-Boundary";
MultipartFormDataContent multiPartContent = new MultipartFormDataContent(boundry);
ByteArrayContent byteArrayContent = new ByteArrayContent(fileContents);
multiPartContent.Add(byteArrayContent);
requestMessage.Content = multiPartContent;
HttpClient httpClient = new HttpClient();
HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage , HttpCompletionOption.ResponseContentRead , CancellationToken.None);
//exception in this line ^
return new Dictionary<string , string>();
}
Run Code Online (Sandbox Code Playgroud)
呼叫者,召集者:
myDictionary = await Upload(filePath);
Run Code Online (Sandbox Code Playgroud)
控制台应用程序的结构如下:
class Program
{
static void Main(string[] args)
{
MainAsync().Wait();
}
static async Task MainAsync()
{
new MyClass().Start();
}
}
Run Code Online (Sandbox Code Playgroud)
而里面 MyClass:
public async void Start()
{
myDictionary = await Upload(filePath);
}
Run Code Online (Sandbox Code Playgroud)
我想我没有正确使用异步,你能看到我错过了什么吗?有任何想法吗?
我 99% 肯定这个错误是由于超时,或者你实际上没有等待你的Start方法MainAsync
我已经解决了以下代码中的超时问题以及其他一些小的更改,这些更改不一定能回答您的问题,但希望对您有所帮助
class Program
{
private static HttpClient httpClient;
static void Main(string[] args)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("your base url");
// add any default headers here also
httpClient.Timeout = new TimeSpan(0, 2, 0); // 2 minute timeout
MainAsync().Wait();
}
static async Task MainAsync()
{
await new MyClass(httpClient).StartAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
我在这里所做的是HttpClient从您的Upload()方法中移出,因为此类旨在多次重用。我已将httpClient对象传递给MyClass的构造函数,您将在下一个代码片段中看到它。
我也改MainAsync()到await了StartAsync(改名从开始到StartAsync,因为它的约定,以后缀异步方法),因为你原来的代码MainAsync()实际上并没有任何等待
正如我在评论中所提到的,您可以更改MainAsync().Wait()到await MainAsync(),如果你改变Main是static async Task Main,这将需要您将构建语言更改为C#7.1或以上
public class MyClass
{
private Dictionary<string, string> myDictionary;
private readonly HttpClient httpClient;
public MyClass(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task StartAsync()
{
myDictionary = await UploadAsync("some file path");
}
public async Task<Dictionary<string, string>> UploadAsync(string filePath)
{
byte[] fileContents;
using (FileStream stream = File.Open(filePath, FileMode.Open))
{
fileContents = new byte[stream.Length];
await stream.ReadAsync(fileContents, 0, (int)stream.Length);
}
HttpRequestMessage requestMessage = new HttpRequestMessage();
// your request stuff here
HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
// parse response and return the dictionary
}
}
Run Code Online (Sandbox Code Playgroud)
在MyClass我做了以下更改
HttpClient向类的构造函数添加了一个参数,因此我们可以将全局 HttpClient 对象传递给此类以供其重用(这就是我们在 中所做的MainAsync())
如前所述,我已将Startand重命名Upload为StartAsyncandUploadAsync因为将 async 方法后缀为 Async 是一种很好的做法
Start已从更改void为 aTask 因为您应该仅async void用于事件处理程序
我改变了你的文件读取方式也是异步的,因为有一种async方法会阻塞 CPU 等待File.ReadAllBytes完成似乎很浪费。在可能的情况下,您应该对 I/O 使用 async/await。
| 归档时间: |
|
| 查看次数: |
15392 次 |
| 最近记录: |