2011-06-02 64 views
7

我正在使用Amazon SDK,並且我有一種方法可爲存儲在Amazon S3服務中的對象返回一個Stream。處理響應後與Amazon S3響應流配合使用

它包含了這樣的事情:

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

顯然,這樣做的時候,流因爲請求對象已被釋放,當做到這一點,它關閉了流不是由調用方法可讀。

我不想將文件下載到MemoryStream或FileStream。

如果我不使用using子句,那麼垃圾收集器將在某個點處置請求對象,所以我不能不使用它。

我問的是,有沒有辦法將流包裝或複製到另一個流,然後返回它,而無需下載文件?

我正在使用.NET 3.5。

編輯:該方法是從一個抽象類繼承而來的,而調用者方法並不知道它正在與Amazon一起工作。所以它必須返回一個Stream。

回答

11

流處理後您不能使用流,但可以推遲處理響應對象,直到使用響應流爲止。有三個選項我可以建議。

  1. Return Response。一種選擇是將響應對象返回給調用者。調用者可以訪問包含的響應流,然後在完成時處置響應。這是最簡單的改變,但要求調用者也改變。

  2. 包裝流。不是直接返回響應流,而是創建一個擴展Stream幷包裝響應流的新對象,但也具有對響應本身的引用。然後,當你的包裝器被處置時,你可以在內部處理響應對象。只要返回的類型只是Stream,就只需要更改被調用的代碼。

  3. 回調。不要從方法返回任何內容,而是使用回調機制。接受一個Action<Stream>作爲參數,並使用該流調用此回調。這樣調用者代碼被調用,並且您仍然可以控制處理響應和流。這是最安全的機制,因爲您不依賴調用者來處理任何事情,但需要進行最多的更改。

+0

我會嘗試第二個選項,如果有效,我會告訴你。 – spakinz 2011-06-02 12:02:01

+1

它工作完美,謝謝@Samuel – spakinz 2011-06-02 12:14:06

+0

我引用第二和第三選項,但請注意第三!是的,它是最安全的,因爲它在回調後立即處理資源,但不能將流保存在操作之外,因爲它是參考。我認爲這對於不瞭解lambda函數範圍的同事來​​說更容易出錯。 我最好的是第二個。我希望新手程序員也能理解接口IDisposable的含義。您在代碼中使用的每個班級都必須檢查它是否是一次性的且管理良好。 – 2017-06-28 14:31:42

1

如果函數返回response對象(不使用using語句),並且調用方將其分配給變量,則仍會引用response對象。因此它不會被收集到符合條件的垃圾。

+0

謝謝蒂姆,但我忘了提及該方法的名稱是GetFileStream,它必須返回一個流。 – spakinz 2011-06-02 11:26:01

+0

@spakinz,你可以改變方法爲'GetAmazonResponseThatContainsFileStream'。方法名稱無關緊要,這個概念是正確的。一種解決方案是讓調用者處理資源。 – 2011-06-02 11:52:04

+1

@Samuel這不是可行的,因爲該方法是從一個抽象類繼承而來的,調用者方法並不知道它與Amazon一起工作。 – spakinz 2011-06-02 11:58:13

1

有同樣的問題(我想)。但你有沒有試過不使用「使用」。這不會使用該流並將其發送給將負責處理它的呼叫者。就如此容易。

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; 
0

爲了完整,因爲我與@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); 
    } 
} 
3

TransferUtility類中有一個方法OpenStream,它返回一個流對象。

公共流的OpenStream( 字符串bucketName, String鍵 )

我通過AWSSDK的源代碼看,並在SDK中的OpenStream只需直接返回響應流中。 (它不會在響應對象上使用「使用」語句。)

+0

這個解決方案讓我非常頭疼!我有一個MVC應用程序,這個解決方案讓我可以一路返回流。 – 2017-06-09 19:26:20

+0

@Jun Y. - 這是下載文件的內容到服務器的內存(或者只是一個「未觸及」的流應該打開嗎?謝謝! – 2017-10-18 20:16:49

0

要將代碼示例添加到@Jun Y.的答案。這就是我所做的,這是迄今爲止最乾淨的解決方案。

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