【问题标题】:Work with an Amazon S3 response stream after response has been disposed处理响应后使用 Amazon S3 响应流
【发布时间】:2011-06-02 11:19:57
【问题描述】:

我正在使用 Amazon 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。

编辑: 该方法继承自抽象类,调用者方法不知道它正在与 Amazon 一起使用。所以它必须返回一个 Stream。

【问题讨论】:

    标签: c# asp.net stream amazon-s3


    【解决方案1】:

    您无法在释放后使用流,但您可以推迟释放响应对象,直到使用响应流之后。我可以建议三个选项。

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

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

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

    【讨论】:

    • 我将尝试第二个选项,如果它有效,我会告诉你。
    • 我引用了第二和第三个选项,但要注意第三个!是的,这是最安全的,因为它会在回调之后立即处理资源,但是您不能将流保存在操作之外,因为它是一个参考。我认为这对于不了解 lambda 函数范围的同事来​​说更容易出错。我觉得最好的是第二个。我希望新手程序员也能理解 IDisposable 接口的含义。您在代码中使用的每个类都必须检查它是否是一次性的并且管理良好。
    【解决方案2】:

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

    公共流 OpenStream( 字符串存储桶名称, 字符串键 )

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

    【讨论】:

    • 这个解决方案让我非常头疼!我有一个 MVC 应用程序,这个解决方案让我可以完全返回流。
    • @Jun Y. - 这是将文件的内容下载到服务器的内存(还是只是应该打开的“未触及”流?谢谢!
    【解决方案3】:

    如果您的函数返回response 对象(不使用using 语句),并且调用者将其分配给一个变量,那么仍然会有对response 对象的引用。因此它不会被符合条件的垃圾收集。

    【讨论】:

    • 谢谢蒂姆,但我忘了提到方法的名称是 GetFileStream,它必须返回一个 Stream。
    • @spakinz,你可以把方法改成GetAmazonResponseThatContainsFileStream。方法名无所谓,概念正确。一种解决方案是让调用者处理资源。
    • @Samuel 这不可能,因为该方法是从抽象类继承的,而调用者方法不知道它正在与 Amazon 一起使用。
    【解决方案4】:

    有同样的问题(我想)。但是您是否尝试过不使用“使用”。这不会使用流并将其发送给负责处理它的调用者。就这么简单。

        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;
    

    【讨论】:

      【解决方案5】:

      为了完整起见,由于我选择了@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);
          }
      }
      

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          猜你喜欢
          • 2013-10-07
          • 1970-01-01
          • 2020-06-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-17
          • 1970-01-01
          • 2013-04-23
          相关资源
          最近更新 更多