zoukankan      html  css  js  c++  java
  • C# 中 async/await 调用传统 Begin/End 异步方法

    最近在改进园子的图片上传程序,希望实现用户上传图片时同时将图片文件保存在三个地方: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(1);
        uploadStream.Position = 0;
        _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(1);
        uploadStream.Position = 0;
        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(1);
        uploadStream.Position = 0;
    
        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;
    }
  • 相关阅读:
    android ListView布局之一(继承listActivity、使用arrayAdapter)
    android your project contains error
    wojilu系统的ORM代码解析[源代码结构分析,ObjectBase基类分析]
    ORM中启用数据库事务
    我记录网站综合系统 技术原理解析[11:ActionProcessor流程wojilu核心]
    互联网,让我们更安全了,还是更危险了【纯讨论】
    不用服务器也能跑的框架wojilu续篇
    使用wojilu 无代码实现 输入框提示 及其背后的原理
    wojilu日志系统可以单独使用
    “我有什么” 和 “你要什么” 框架制作的一些思考
  • 原文地址:https://www.cnblogs.com/dudu/p/async_await_call_begin_end_method.html
Copyright © 2011-2022 走看看