天天看點

由Stream.Position問題而引發的思考

    Web開發中的檔案上傳功能一直以來都是一個比較棘手的問題,特别是在開發Ajax網站時,面對功能複雜的頁面元素,實作頁面無重新整理的檔案上傳功能就變得更加複雜。我在 http://www.cnblogs.com/jaxu/archive/2009/05/19/1459796.html一文中介紹過如何通過頁面中隐藏的iFrame送出表單達到檔案的上傳,而且不重新整理當頁中的Form,模拟頁面的無重新整理檔案上傳功能。而且一般情況下,我們在服務端會這樣來處理要上傳的檔案。 private  List < ImageEntity >  GetUploadImages()

{

    List < ImageEntity >  images  =   new  List < ImageEntity > ();

    HttpFileCollection files  =  Request.Files;

     int  fileLen;

    ImageEntity image  =   null ;

     if  (files  !=   null   &&  files.Count  >   0 )

    {

         for  ( int  i  =   0 ; i  <  files.Count; i ++ )

        {

             if  (files[i].FileName.Length  !=   0 )

            {

                 try

                {

                    fileLen  =  files[i].ContentLength;

                    Byte[] bytes  =   new  Byte[fileLen];

                    stream.Read(bytes,  0 , fileLen);

                    image  =   new  ImageEntity();

                    image.Type  =  files[i].ContentType;

                    image.ImageBlob  =  bytes;

                    image.Title  =  Path.GetFileName(files[i].FileName);

                    images.Add(image);

                }

                 catch  { }

            }

        }

    }

     return  images;

}

    這是一個得到用戶端上傳的圖檔檔案的示例代碼,其中的ImageEntity是一個Image的實體類,我們可以通過該實體類描述的資訊将圖檔存儲在資料庫中(或者存儲在伺服器磁盤上)。 public   class  ImageEntity

{

     public  ImageEntity()

    {

    }

     public  ImageEntity( string  title, Byte[] imageBlob,  string  type)

    {

        Title  =  title;

        ImageBlob  =  imageBlob;

        Type  =  type;

    }

     public   string  Title {  get ;  set ; }

     public   string  Type {  get ;  set ; }

     public  Byte[] ImageBlob {  get ;  set ; }

}

    這麼使用是沒有問題的!當我們上傳圖檔時,頁面通過Post方法送出到服務端,服務端構造一個和圖檔位元組大小相同的byte數組,通過HttpPostedFile的InputStream屬性得到一個System.IO.Stream對象,然後使用該對象的Read方法将圖檔資料流讀到byte數組中。這個過程頁面是需要回傳的,并且下一次使用者上傳圖檔時Stream對象會被重新構造,然後重新填充byte數組。

    不過在一次MOSS開發中我偶然地發現使用該方式上傳圖檔時,儲存圖檔到資料庫沒有出現問題,當從資料庫中讀取圖檔時卻顯示了一個紅色的叉,表示圖檔不可用或加載失敗。仔細檢視了一下資料庫中的資料,除了儲存圖檔二進制資料的字段顯示為<Binary data>外,其餘字段的資料都很正常,沒有發現什麼異常情況,調試了一下程式,圖檔上傳和儲存的過程中并沒有抛出任何異常,一切都很順利,但就是在讀取圖檔的時候頁面上無法正常加載圖檔。

    一開始我就感覺這個問題很奇怪,看來問題應該是出在上傳圖檔時圖檔的二進制資料擷取得不正确或不完整,我再次調試跟蹤了一下代碼,發現在上傳的過程中byte數組中的值始終都是0,即便是在通過Stream.Read方法填充資料之後也是如此。這到底是為什麼?難道我用錯對象了?查了一下MSDN,發現上面給出的示例基本上也是通過這種方法得到要上傳的檔案并通過Stream.Read方法填充byte數組的,MSDN的示例代碼肯定是不會有錯誤的,那錯誤到底在哪裡呢?我開始迷惑了...

    仔細搜了搜Google,其中有一位朋友給出的幫助對我很有用,他建議在使用Stream.Read方法填充byte數組前先判斷一下Stream.Position的值是否為0,如果不為0就先将它置為0,然後再進行byte數組的填充。不過我先是想到每次頁面在上傳檔案時所構造的Stream對象都是新的(因為頁面會PostBack回來),我并沒有在程式的任何地方緩存Stream對象,既然Stream對象是新的,那麼Position屬性的值肯定就是0啦。我抱着半信半疑的心态試了試這位朋友介紹的方法,果然奏效了,看來Stream對象真的是在某個地方被緩存了,或者說在前一次檔案上傳之後Stream對象沒有被完全釋放。于是我修改了上面的那個得到上傳圖檔的方法。

private  List < ImageEntity >  GetUploadImages()

{

    List < ImageEntity >  images  =   new  List < ImageEntity > ();

    HttpFileCollection files  =  Request.Files;

     int  fileLen;

    ImageEntity image  =   null ;

     if  (files  !=   null   &&  files.Count  >   0 )

    {

         for  ( int  i  =   0 ; i  <  files.Count; i ++ )

        {

             if  (files[i].FileName.Length  !=   0 )

            {

                 try

                {

                    fileLen  =  files[i].ContentLength;

                    Byte[] bytes  =   new  Byte[fileLen];

                     using  (Stream stream  =  files[i].InputStream)

                    {

                        stream.Position  =   0 ;

                        stream.Read(bytes,  0 , fileLen);

                    }

                    image  =   new  ImageEntity();

                    image.Type  =  files[i].ContentType;

                    image.ImageBlob  =  bytes;

                    image.Title  =  Path.GetFileName(files[i].FileName);

                    images.Add(image);

                }

                 catch  { }

            }

        }

    }

     return  images;

}

    因為我是在MOSS平台上開發的,與普通的ASP.NET項目就有很多的不同,有可能會受到很多MOSS本身的東西影響,例如緩存機制等。但不管怎樣,涉及到像Stream這樣的對象,在使用之後最好都讓它立即釋放,因為即使Stream不被緩存,它也有可能長時間占用記憶體而消耗掉很多的伺服器資源。建議在Using語句中使用Stream,并且在使用Read方法填充byte數組前重置Position為0,這樣可以確定byte數組被正确填充,進而保證可以得到正确的檔案資料。

轉載于:https://www.cnblogs.com/jaxu/archive/2009/05/19/1460250.html