处理完响应后,使用Amazon S3响应流

我正在使用亚马逊SDK,我有一个方法可以返回存储在Amazon S3服务中的对象的Stream。

它包含这样的东西:

var request = new GetObjectRequest().WithBucketName(bucketName).WithKey(keyName); using (var response = client.GetObject(request)) { return response.ResponseStream; } 

显然,在执行此操作时,流不能从调用方法中读取,因为已经处理了请求对象,并且当完成此操作时,它将关闭流。

我不想将文件下载到MemoryStream或FileStream。

如果我不使用using子句,垃圾收集器将在某个时候处置请求对象,所以我不能不使用它。

我要问的是,有没有办法将Stream包装或复制到另一个Stream然后返回它而不必下载文件?

我正在使用.NET 3.5。

编辑:该方法inheritance自抽象类,调用方法不知道它与Amazon一起使用。 所以它有一个返回流。

在处理完流后,您无法使用流,但是可以推迟处理响应对象,直到使用响应流为止。 我可以建议三种选择。

  1. 回复。 一种选择是将响应对象返回给调用者。 调用者可以访问包含的响应流,然后在完成后处理响应。 这是最简单的更改,但也需要调用者进行更改。

  2. 包裹流。 不是直接返回响应流,而是创建一个新对象,该对象扩展Stream并包装响应流,但也有一个对响应本身的引用。 然后,当您的包装器被丢弃时,您可以在内部处置响应对象。 这只需要更改被调用的代码,只要返回的类型只是Stream

  3. 打回来。 不要从方法返回任何内容并使用回调机制。 接受Action作为参数,并使用流调用此回调。 这样调用了调用者代码,您仍然可以控制处理响应和流。 这是最安全的机制,因为您不依赖于调用者处理任何内容,但需要进行大多数更改。

TransferUtility类中有一个方法OpenStream,它返回一个流对象。

public Stream OpenStream(String bucketName,String key)

我查看了AWSSDK的源代码,并在SDK中发现OpenStream只是直接返回响应流。 (它不对响应对象使用“using”语句。)

如果函数返回response对象(不使用using语句),并且调用者将其分配给变量,则仍会有对response对象的引用。 因此它不符合资格垃圾收集。

有同样的问题(我想)。 但你有没有试过不使用“使用”。 这不会使用流并将其发送给有责任处理它的调用者。 就如此容易。

  var request = new GetObjectRequest { BucketName = containerName, Key = blobName }; GetObjectResponse response = null; try { response = client.GetObject(request)); } catch (AmazonS3Exception ex) { if ((ex.ErrorCode == "NoSuchBucket") || (ex.ErrorCode == "AccessDenied") || (ex.ErrorCode == "InvalidBucketName") || (ex.ErrorCode == "NoSuchKey")) { return null; } throw; } return response.ResponseStream; 

为了完整性,自从我使用@Samuel选项2(包裹流)@spakinz评论他也做了,我在这里包含了我所谓的AmazonS3Stream

 public class AmazonS3Stream : Stream { private Stream stream; private GetObjectResponse response; public AmazonS3Stream(GetObjectResponse response) { this.stream = response.ResponseStream; this.response = response; } // The whole purpose of this class protected override void Dispose(bool disposing) { // base.Dispose(disposing); // Do we really need this line? Probably not since I tested it and I can see that the stream is disposed when Response.Dispose is called by itself. And that makes sense because we know that this.stream is pointing to response.ResponseStream (that's what we declared in the constructor: this.stream = response.ResponseStream; ) So, what do we expect from response.Dispose() ? Obviously the first thing it would do is call ResponseStream.Dispose() response.Dispose(); } public override long Position { get { return stream.Position; } set { stream.Position = Position; } } public override long Length { get { return stream.Length; } } public override bool CanRead { get { return stream.CanRead; } } public override bool CanSeek { get { return stream.CanSeek; } } public override bool CanWrite { get { return stream.CanWrite; } } public override void Flush() { stream.Flush(); } public override void Write(byte[] buffer, int offset, int count) { stream.Write(buffer, offset, count); } public override void SetLength(long value) { stream.SetLength(value); } public override long Seek(long offset, SeekOrigin origin) { return stream.Seek(offset, origin); } public override int Read(byte[] buffer, int offset, int count) { return stream.Read(buffer, offset, count); } } 

将代码示例添加到@Jun Y.的答案。 这就是我所做的,这是迄今为止最干净的解决方案。

 using (var client = new AmazonS3Client(Amazon.RegionEndpoint.USEast2)) { var transferUtility = new TransferUtility(client); return await transferUtility.OpenStreamAsync(S3BucketName, key); }