上传文件无需等待吗?

huangapple go评论51阅读模式
英文:

Upload file without waiting for it?

问题

以下是翻译好的部分:

我猜这更多地涉及异步和等待的问题,但在我的应用程序的一部分中,我必须获取外部链接,然后基本上将其上传到我的CDN,因为链接在一定时间后会过期。

文件的上传,特别是远程文件,速度相当慢,所以我想将任务放到后台,不必担心它花了多长时间,只要我有一个URL。

我创建了一个存储客户端,像这样:

public class SpacesStorageClient : IStorageClient
{
    private readonly AmazonS3Client _s3Client;
    private readonly string _bucketName;
    private readonly string _cdnUrl;

    public SpacesStorageClient(AmazonS3Client s3Client, string bucketName, string cdnUrl)
    {
        _s3Client = s3Client;
        _bucketName = bucketName;
        _cdnUrl = cdnUrl;
    }

    public async Task<string> UploadRemoteFileAsync(string url, string directory, string fileName, bool publicRead)
    {
        var stream = await HttpUtilities.GetStreamFromUrlAsync(url);
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }

    public async Task<string> UploadStreamAsync(Stream stream, directory, fileName, publicRead);
    {
        var transferUtility = new TransferUtility(_s3Client);

        var uploadRequest = new TransferUtilityUploadRequest
        {
            BucketName = _bucketName,
            Key = $"{directory}/{fileName}",
            InputStream = stream,
            CannedACL = publicRead ? S3CannedACL.PublicRead : S3CannedACL.Private
        };

        await transferUtility.UploadAsync(uploadRequest);
        return $"{_cdnUrl}/{directory}/{fileName}";
    }

    public async Task<string> UploadByteArrayAsync(byte[] bytes, string directory, string fileName, bool publicRead)
    {
        var stream = new MemoryStream(bytes);
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }
}

但是文件上传时间太长,我希望使UploadRemoteFileAsync智能化,不必等待,因为我的应用程序不需要立即使用新链接。

通过了解它如何构建CDN URL,我认为这是可能的,但在等待方面我陷入了困境。

是否可以简单地在UploadStreamAsync之前或其中删除await,就像这样?

英文:

I'm guessing this is more of a question around async and await, but part of my application I have to take an external link, and basically upload it to my own CDN as the links expire after a set time.

The uploading of files, remote files to be specific is rather slow so I would like to background the task and not have to worry about how long its taken, as long as I have a URL to it.

I created a storage client like:

public class SpacesStorageClient : IStorageClient
{
    private readonly AmazonS3Client _s3Client;
    private readonly string _bucketName;
    private readonly string _cdnUrl;

    public SpacesStorageClient(AmazonS3Client s3Client, string bucketName, string cdnUrl)
    {
        _s3Client = s3Client;
        _bucketName = bucketName;
        _cdnUrl = cdnUrl;
    }

    public async Task&lt;string&gt; UploadRemoteFileAsync(string url, string directory, string fileName, bool publicRead)
    {
        var stream = await HttpUtilities.GetStreamFromUrlAsync(url);
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }

    public async Task&lt;string&gt; UploadStreamAsync(Stream stream, string directory, string fileName, bool publicRead)
    {
        var transferUtility = new TransferUtility(_s3Client);
            
        var uploadRequest = new TransferUtilityUploadRequest
        {
            BucketName = _bucketName,
            Key = $&quot;{directory}/{fileName}&quot;,
            InputStream = stream,
            CannedACL = publicRead ? S3CannedACL.PublicRead : S3CannedACL.Private
        };
            
        await transferUtility.UploadAsync(uploadRequest);
        return $&quot;{_cdnUrl}/{directory}/{fileName}&quot;;
    }

    public async Task&lt;string&gt; UploadByteArrayAsync(byte[] bytes, string directory, string fileName, bool publicRead)
    {
        var stream = new MemoryStream(bytes);
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }
}

But the uploading of files takes far too long, I want to make UploadRemoteFileAsync smart and not have to wait, as my application doesn't need the new link straight away.

By understanding how it constructs CDN url's I believe this is possible but I'm stuck when it comes to the await stuff.

Is it as simple as removing await before UploadStreamAsync or inside it?

答案1

得分: 1

首先,将 _cdnUrl 属性公开访问:

public string CdnUrl => _cdnUrl;

不要等待 UploadRemoteFileAsync,并在不等待的情况下构建 URL:

_storageClient.UploadRemoteFileAsync(sourceUrl, directory, cdnFileName, true);

从属性和其他详细信息构建 URL:

var futureUrl = $"{_storageClient.CdnUrl}/{directory}/{cdnFileName}";
英文:

First, make the _cdnUrl property publicly accessible:

public string CdnUrl =&gt; _cdnUrl;

Don't await UploadRemoteFileAsync, and construct the url without waiting:

_storageClient.UploadRemoteFileAsync(sourceUrl, directory, cdnFileName, true);

Construct URL from property and other details:

var futureUrl = $&quot;{_storageClient.CdnUrl}/{directory}/{cdnFileName}&quot;;

答案2

得分: 1

开始讨论使用情景和限制。您可能希望启动上传操作,但不知道其完成状态,而只需在 UploadStreamAsync 方法的输出中包含您生成的地址。

在这种情况下,您可以只移除对实际文件下载方法 transferUtility.UploadAsync(uploadRequest)await,并仍然通过 UploadStreamAsync 方法返回结果,以在类内部保留地址分配逻辑:

public class SpacesStorageClient : IStorageClient
{
    private readonly AmazonS3Client _s3Client;
    private readonly string _bucketName;
    private readonly string _cdnUrl;

    public SpacesStorageClient(AmazonS3Client s3Client, string bucketName, string cdnUrl)
    {
        _s3Client = s3Client;
        _bucketName = bucketName;
        _cdnUrl = cdnUrl;
    }

    public async Task<string> UploadRemoteFileAsync(string url, string directory, string fileName, bool publicRead)
    {
        var stream = await HttpUtilities.GetStreamFromUrlAsync(url);
        // 我们在这里使用 await,但在 UploadStreamAsync 方法内部不等待任何操作完成,因此实际上不会等待
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }

    public async Task<string> UploadStreamAsync(Stream stream, string directory, string fileName, bool publicRead)
    {
        var transferUtility = new TransferUtility(_s3Client);

        var uploadRequest = new TransferUtilityUploadRequest
        {
            BucketName = _bucketName,
            Key = $"{directory}/{fileName}",
            InputStream = stream,
            CannedACL = publicRead ? S3CannedACL.PublicRead : S3CannedACL.Private
        };

        // 我们执行上传,但不等待操作结束,继续执行下一行代码
        transferUtility.UploadAsync(uploadRequest);
        return $"{_cdnUrl}/{directory}/{fileName}";
    }

    public async Task<string> UploadByteArrayAsync(byte[] bytes, string directory, string fileName, bool publicRead)
    {
        var stream = new MemoryStream(bytes);
        // 我们在这里使用 await,但在 UploadStreamAsync 方法内部不等待任何操作完成,因此实际上不会等待
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }
}

此外,您可以移除地址返回逻辑并在代码中移除 async/await 的使用。这样,您将手动处理任务。在这种情况下,用户将获得上传任务,如果他愿意,可以通过 await 等待其完成,或者如果结果不重要,可以丢弃此任务。

public class SpacesStorageClient : IStorageClient
{
    private readonly AmazonS3Client _s3Client;
    private readonly string _bucketName;
    private readonly string _cdnUrl;

    public SpacesStorageClient(AmazonS3Client s3Client, string bucketName, string cdnUrl)
    {
        _s3Client = s3Client;
        _bucketName = bucketName;
        _cdnUrl = cdnUrl;
    }

    public Task UploadRemoteFileAsync(string url, string directory, string fileName, bool publicRead)
    {
        var stream = await HttpUtilities.GetStreamFromUrlAsync(url);
        return UploadStreamAsync(stream, directory, fileName, publicRead);
    }

    public Task UploadStreamAsync(Stream stream, string directory, fileName, bool publicRead)
    {
        var transferUtility = new TransferUtility(_s3Client);

        var uploadRequest = new TransferUtilityUploadRequest
        {
            BucketName = _bucketName,
            Key = $"{directory}/{fileName}",
            InputStream = stream,
            CannedACL = publicRead ? S3CannedACL.PublicRead : S3CannedACL.Private
        };

        return transferUtility.UploadAsync(uploadRequest);
    }

    public async Task UploadByteArrayAsync(byte[] bytes, string directory, string fileName, bool publicRead)
    {
        var stream = new MemoryStream(bytes);
        return UploadStreamAsync(stream, directory, fileName, publicRead);
    }
}

请记住,在这种情况下,您还需要更改接口 IStorageClient 的契约。此外,您仍然可以访问上传任务并与其一起工作:

SpacesStorageClient client = GetSpacesStorageClient();
var uploadingTask = client.UploadRemoteFileAsync(someUrl, someDirectory, someFileName, publicRead);
// 等待上传完成
await uploadingTask;
// 阻塞等待上传完成
uploadingTask.Wait();

// 检查上传是否失败
if (uploadingTask.Status == TaskStatus.Faulted)
{
    // 任务完成时出现异常
    Exception ex = uploadingTask.Exception;
    // 在这里处理异常
}
英文:

Let's start with usage scenarios and limitations. You probably want to start the upload, not know its completion status, but have the address you generate in the output of the UploadStreamAsync method.

In that case, you can remove await only for the actual file download method transferUtility.UploadAsync(uploadRequest) and still return the result with the address from the UploadStreamAsync method to leave the address assignment logic inside the class:

public class SpacesStorageClient : IStorageClient
{
    private readonly AmazonS3Client _s3Client;
    private readonly string _bucketName;
    private readonly string _cdnUrl;

    public SpacesStorageClient(AmazonS3Client s3Client, string bucketName, string cdnUrl)
    {
        _s3Client = s3Client;
        _bucketName = bucketName;
        _cdnUrl = cdnUrl;
    }

    public async Task&lt;string&gt; UploadRemoteFileAsync(string url, string directory, string fileName, bool publicRead)
    {
        var stream = await HttpUtilities.GetStreamFromUrlAsync(url);
        // we use await here but inside method UploadStreamAsync we do not
        // wait any operation to complete, so there will be no waiting actually
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }

    public async Task&lt;string&gt; UploadStreamAsync(Stream stream, string directory, string fileName, bool publicRead)
    {
        var transferUtility = new TransferUtility(_s3Client);
            
        var uploadRequest = new TransferUtilityUploadRequest
        {
            BucketName = _bucketName,
            Key = $&quot;{directory}/{fileName}&quot;,
            InputStream = stream,
            CannedACL = publicRead ? S3CannedACL.PublicRead : S3CannedACL.Private
        };

        // we run Uploading but don&#39;t wait the end of operation and go to the next line of code    
        transferUtility.UploadAsync(uploadRequest);
        return $&quot;{_cdnUrl}/{directory}/{fileName}&quot;;
    }

    public async Task&lt;string&gt; UploadByteArrayAsync(byte[] bytes, string directory, string fileName, bool publicRead)
    {
        var stream = new MemoryStream(bytes);
        // we use await here but inside method UploadStreamAsync we do not
        // wait any operation to complete, so there will be no waiting actually
        return await UploadStreamAsync(stream, directory, fileName, publicRead);
    }
}

Also you can remove the address return logic and remove the use of async/await in the code. So you will manipulate the tasks maually. In this case the user will get back the upload task, for which he can wait to finish via await if he wants or discard this task if the result is not important.

public class SpacesStorageClient : IStorageClient
{
    private readonly AmazonS3Client _s3Client;
    private readonly string _bucketName;
    private readonly string _cdnUrl;

    public SpacesStorageClient(AmazonS3Client s3Client, string bucketName, string cdnUrl)
    {
        _s3Client = s3Client;
        _bucketName = bucketName;
        _cdnUrl = cdnUrl;
    }

    public Task UploadRemoteFileAsync(string url, string directory, string fileName, bool publicRead)
    {
        var stream = await HttpUtilities.GetStreamFromUrlAsync(url);
        return UploadStreamAsync(stream, directory, fileName, publicRead);
    }

    public Task UploadStreamAsync(Stream stream, string directory, string fileName, bool publicRead)
    {
        var transferUtility = new TransferUtility(_s3Client);
            
        var uploadRequest = new TransferUtilityUploadRequest
        {
            BucketName = _bucketName,
            Key = $&quot;{directory}/{fileName}&quot;,
            InputStream = stream,
            CannedACL = publicRead ? S3CannedACL.PublicRead : S3CannedACL.Private
        };
            
        return transferUtility.UploadAsync(uploadRequest);
    }

    public async Task UploadByteArrayAsync(byte[] bytes, string directory, string fileName, bool publicRead)
    {
        var stream = new MemoryStream(bytes);
        return UploadStreamAsync(stream, directory, fileName, publicRead);
    }
}

Remember that in this case you need to change the contract for the interface IStorageClient too. And you still have an access to uploading task and can work with it:

SpacesStorageClient client = GetSpacesStorageClient();
var uploadingTask = client.UploadRemoteFileAsync(someUrl, someDirectory, someFileName, publicRead);
// wait when uploading is finished
await uploadingTask;
// wait when uploading is finished with blocking
uploadingTask.Wait();

// check if uploading is failed 
if (uploadingTask.Status == TaskStatus.Faulted)
{
    // The task completed with an exception
    Exception ex = uploadingTask.Exception;
    // Handle the exception here
}

huangapple
  • 本文由 发表于 2023年2月26日 23:11:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75572912.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定