最近在改进园子的图片上传程序,希望实现用户上传图片时同时将图片文件保存在三个地方:1)服务器本地硬盘;2)又拍云;3)阿里云OSS。并且在保存时使用异步操作。

对于异步保存到本地硬盘,只需用 Steam.CopyToAsync() 将上传文件流异步复制到 FileStream 即可。

对于异步保存至又拍云,只要借助 WebRequest.GetRequestStreamAsync() + Steam.CopyToAsync() 就可以实现。

而阿里云OSS提供了 .NET SDK,使用起来很方便,但是之前并没有提供异步接口,成为异步化的一个障碍。

今天在 OSS .NET SDK 的更新日志中惊喜地发现:“添加异步化接口(支持Put/Get/List/Copy/PartCopy等异步操作)”。于是立马下载下来,可是一使用惊喜瞬间化为乌有 —— 新版 SDK 只提供了传统的 Begin/End 异步接口,却没有提供 async 异步接口。

public IAsyncResult BeginPutObject(string bucketName, string key, Stream content, AsyncCallback callback, object state);
public PutObjectResult EndPutObject(IAsyncResult asyncResult);

怀着失落的心情,望着孤零零的没有 await 陪伴的 async,心里有说不出的滋味。。。

async Task<bool> IBucket.PutFileAsync(string filePath, Stream uploadStream)
{
filePath = filePath.Substring();
uploadStream.Position = ;
_client.PutObject(_bucketName, filePath, uploadStream);
return true;
}

难道这次只能实现半吊子的异步化吗?好不容易等来 OSS .NET SDK 支持异步化,难道只是空欢喜一场吗?真有些不甘心啊!

这时,心中突然闪过一个念头:有没有可能直接用 async/await 调用 Begin/End 异步方法?也许微软早就为我们准备好了馅饼?

于是,在网上搜寻了一番,发现了一线希望 —— 用 Task.Factory.FromAsync() 是可能实现的。

可是,一堆 FromAsync 方法看着就让人晕,只能一点点去试。

开始用的是 Task.Factory.FromAsync<PutObjectResult> ,但参数总对不上,比如:

await Task.Factory.FromAsync<PutObjectResult>(
_client.BeginPutObject,
_client.EndPutObject,
_bucketName, filePath, uploadStream,
null);

编译出错:

No overload for method 'FromAsync' takes 6 arguments

后来改为下面这样,总算编译通过了:

await Task.Factory.FromAsync<PutObjectResult>(
_client.BeginPutObject(_bucketName, filePath, uploadStream, null, null),
x => { return _client.EndPutObject(x); });

而且运行程序,图片都能成功上传到阿里云OSS中,但总是报这样的错误:

failed: System.ArgumentException : retryableAsyncResult should not be null
at Aliyun.OpenServices.OpenStorageService.Utilities.OssUtils.EndOperationHelper(IServiceClient serviceClient, IAsyncResult asyncResult)

这个错误说明 callback 调用没成功。

在这个地方折腾了很长时间,后来瞎猫碰着死耗子,把 Task.Factory.FromAsync<PutObjectResult> 改为 Task<PutObjectResult>.Factory.FromAsync 问题就解决了。代码如下:

async Task<bool> IBucket.PutFileAsync(string filePath, Stream uploadStream)
{
filePath = filePath.Substring();
uploadStream.Position = ;
var result = await Task<PutObjectResult>.Factory.FromAsync(
_client.BeginPutObject,
_client.EndPutObject,
_bucketName, filePath, uploadStream,
null);
Console.WriteLine(result.ETag);
return true;
}

【2017-7-25更新】

感谢评论中 @唐诗 的建议!TaskCompletionSource 的确是更好的解决方法,最新的实现代码如下:

Task<bool> IBucket.PutFileAsync(string filePath, Stream uploadStream, string contentType)
{
filePath = filePath.Substring();
uploadStream.Position = ; var tcs = new TaskCompletionSource<bool>(); _client.BeginPutObject(_bucketName,
filePath,
uploadStream,
new ObjectMetadata { ContentType = contentType },
asyncResult =>
{
_client.EndPutObject(asyncResult);
tcs.SetResult(true);
},
null); return tcs.Task;
}

C# 中 async/await 调用传统 Begin/End 异步方法的更多相关文章

  1. C#中async/await中的异常处理

    在同步编程中,一旦出现错误就会抛出异常,我们可以使用try-catch来捕捉异常,而未被捕获的异常则会不断向上传递,形成一个简单而统一的错误处理机制.不过对于异步编程来说,异常处理一直是件麻烦的事情, ...

  2. [翻译] Python 3.5中async/await的工作机制

    Python 3.5中async/await的工作机制 多处翻译出于自己理解,如有疑惑请参考原文 原文链接 身为Python核心开发组的成员,我对于这门语言的各种细节充满好奇.尽管我很清楚自己不可能对 ...

  3. 关于C#中async/await中的异常处理(下)-(转载)

    上一篇文章里我们讨论了某些async/await的用法中出现遗漏异常的情况,并且谈到该如何使用WhenAll辅助方法来避免这种情况.WhenAll辅助方法将会汇总一系列的任务对象,一旦其中某个出错,则 ...

  4. 关于C#中async/await中的异常处理(上)-(转载)

    在同步编程中,一旦出现错误就会抛出异常,我们可以使用try…catch来捕捉异常,而未被捕获的异常则会不断向上传递,形成一个简单而统一的错误处理机制.不过对于异步编程来说,异常处理一直是件麻烦的事情, ...

  5. 关于C#中async/await中的异常处理(上)

    关于C#中async/await中的异常处理(上) 2012-04-11 09:15 by 老赵, 17919 visits 在同步编程中,一旦出现错误就会抛出异常,我们可以使用try…catch来捕 ...

  6. Vue实例中封装api接口的思路 在页面中用async,await调用方法请求

    一般我们写小型的项目是用不到封装axios实例 但是当我们写大型项目时  接口有时候多到有上百个接口,那我们在请求一次调用一次接口,接口上好多都是重复的,这个时候我们就可以封装axios实例,既节省了 ...

  7. 关于 ASP.NET Web 应用中 async/await 注意问题

    System.NullReferenceException: Object reference not set to an instance of an object. at System.Web.T ...

  8. springcloud中servcie层调用fegin异常以及异步方法的实现

    近日在做业务上的短信推送和APP消息推送,通过调用别的模块的接口来实现,在springcloud中通过fegin进行调用.这里要说明的事情并不是如何开发推送功能,而是在调试过程中碰到的一些小问题.我把 ...

  9. js中回调函数,promise 以及 async/await 的对比用法 对比!!!

    在编程项目中,我们常需要用到回调的做法来实现部分功能,那么在js中我们有哪些方法来实现回调的? 方法1:回调函数 首先要定义这个函数,然后才能利用回调函数来调用! login: function (f ...

随机推荐

  1. centos 6 initctl

    在centos6中有initctl 可以启动tty等.此命令在 upstart-0.6.5-10.el6.x86_64 rpm包中 或者 mingetty 命令启动 tty #initctl star ...

  2. 【转载】C++ IO库

    本篇随笔为转载,原贴地址:<C++ Primer>第8章 IO库 学习笔记. 1.IO类 #include <iostream> istream//从流中读取数据 ostrea ...

  3. Android,配置Activity为启动Activity(AndroidManifest.xml,application,intent-filter,MAIN,LAUNCHER)

    备忘: 将Activity注册为启动Activity. 在AndroidManifest.xml中的<application>元素中加入以下<activity>子元素内容: & ...

  4. docker网络配置之自定义网桥

    使用特定范围的 IP (仅适用于v1.x)不适用于新版的v1.1x Docker 会尝试寻找没有被主机使用的 ip 段,尽管它适用于大多数情况下,但是它不是万能的,有时候我们还是需要对 ip 进一步规 ...

  5. 根据日期 读取三个csv不留指定日期的内容 新保存一个文件

    using System;using System.Collections.Generic;using System.Drawing;using System.Globalization;using ...

  6. Xshell

    http://baike.baidu.com/link?url=5lc5IxVVauitYSbqlOXJBvrvM3qVfMhzz6x_xu2cOMb108Ln9Wk7iJ3f46vG0kBninKw ...

  7. (转载)IOS中UIScrollView的属性和委托方法

    http://www.jizhishusheng.com/?p=453   ---- 以下内容来自 UIScrollView 类负责所有基于 UIKit 的滚动操作一.创建 1. CGRect bou ...

  8. 问题: ActivityManager: Warning: Activity not started, its current task has been brought to the front

    运行程序时看控制台有这样的错误,应用程序没跑起来. 解决办法:project-->Clean

  9. android之RadioGroup

    radioGroup这控件在开发中也是常用到的 RadioGroup 有时候比较有用.主要特征是给用户提供多选一机制. 用微信举一个例子吧! <?xml version="1.0&qu ...

  10. SQL Server Profiler使用方法

    一.SQL Server Profiler使用方法 1.单击开始--程序--Microsoft SQL Server 2005--性能工具--SQL Server Profiler,如下图:   2. ...