最近在改进园子的图片上传程序,希望实现用户上传图片时同时将图片文件保存在三个地方: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. python3抓取到的拉勾数据统计

    趁着最近有时间写了个拉勾爬虫抓取了后端.前端和移动端技术岗位的数据,总共大约6多万条记录,对其取前十名进行统计 按地域划分: 可以看出北上广深杭的数量远远超出其它城市,机会相对较多 2. 按融资阶段来 ...

  2. no 'object' file generated

    c++编译的时候出现如下的错误: no 'object' file generated   [解决方法] Go to project properties -> configurations p ...

  3. 8.Mybatis的延迟加载

    在Mybatis中的延迟加载只有resultMap可以实现,ResultMap 可以实现高级映射(association,collection可以实现一对1和一对多的映射),他们具有延迟加载的功能,r ...

  4. JavaScript实例

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...

  5. blob storage第一次亲密接触

    由于azure上各个云服务都是分开的,所以当一个地方生成文件后,我在别的地方想要读取他就变得困难,不过azure上面有解决方案,就是azure blob storage. 在现有的订阅账号下,新建一个 ...

  6. PLSQL往表中插入中文乱码解决办法

    1.查看服务器端编码:select userenv('language') from dual; 2.查看第一行中PARAMETER项中为NLS_LANGUAGE 对应的VALUE项中是否和第一步得到 ...

  7. Python全栈之路3--set集合--三元运算--深浅拷贝--初识函数

    一.上节课的重点回顾: 1.类名加括号其实就是执行类的__init__方法: 2.int a.创建方式 n1 = 123 #根据int类创建了一个对象 n2 = int(123) #根据int类创建一 ...

  8. main与对象初始化 in C++

    没有学过代码编译的原理,以前也没有兴趣去学编译器的相关原理,但是近期通过阅读google开源项目gtest,对我稍有触动. 代码: main test示例 TEST宏定义 #define TEST(t ...

  9. spring mvc 配置文件信息记录

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  10. 包含min函数的栈

    题目描述 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数 解法一: 思路:采用java中自带的迭代函数进行处理. public class Solution{ /** * @pa ...