由通過WebService上傳附件給SharePoint的List時檔案不能太大的問題說起…

來源:互聯網
上載者:User

     有個項目(Submition project)是利用Silverlight做個表單將資料提交到SharePoint的List裡,表單除了填寫一些資訊外還可以上傳附件,但遇到的問題是上傳附件時檔案如果比較大就上傳失敗。

        該項目的大致思路是用Silverlight收集資料,然後再通過javascript調用SharePoint的相關WebService來上傳資料。相關代碼如下:

  1、Silverlight擷取選擇的待上傳的檔案內容:

  1. OpenFileDialog openFileDialog = new OpenFileDialog();  
  2.  if ((bool)openFileDialog.ShowDialog()) 
  3.    { 
  4.        FileInfo fileInfo = openFileDialog.File; 
  5.        Stream stream = fileInfo.OpenRead(); 
  6.        byte[] buffer = new byte[stream.Length];                                
  7.        stream.Read(buffer, 0, buffer.Length); 
  8.        string strFileContent = Convert.ToBase64String(buffer);                
  9.        stream.Close();                
  10.    }

           即在Silverlight中擷取了選擇的檔案的內容並轉化成了一個字串,以便在JavaScript裡調用。

       2、利用Javascript上傳檔案到SharePoint,即利用SharePoint的WebService Lists.amsx裡的AddAttachment方法:

  1. function AddAttachment(listName, listItemID, fileName, strFileContent){ 
  2.    var xmlrequest; 
  3.    xmlrequest = SoapPrefix + '<AddAttachment xmlns="http://schemas.microsoft.com/sharepoint/soap/"><listName>' + 
  4.                 listName + '</listName><listItemID>' + listItemID + '</listItemID><fileName>' 
  5.                 + fileName + '</fileName><attachment>' + strFileContent + '</attachment></AddAttachment>' 
  6.    xmlrequest += SoapPostfix; 
  7.    if (!xmlhttpLists) 
  8.        xmlhttpLists = GetHTTPObject(); 
  9.   
  10.    xmlhttpLists.open("POST", "http://localhost/_vti_bin/Lists.asmx?op=AddAttachment", false);            
  11.    xmlhttpLists.setRequestHeader("Content-Type", "text/xml; charset=utf-8"); 
  12.    xmlhttpLists.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/sharepoint/soap/AddAttachment"); 
  13.    xmlhttpLists.setRequestHeader("Content-Length", xmlrequest.length);            
  14.    xmlhttpLists.send(xmlrequest);            
  15.    return xmlhttpLists.readyState + " -- " + xmlhttpLists.status; 
  16. }

                    註:listItemID是先前調用WebService的UpdateListItems方法添加一條記錄到SharePoint的List後返回的一個ListID。

    當檔案比較小時上傳能夠成功,但檔案超過30M就上傳失敗,並拋出錯誤“遠程伺服器不可調用”、“The remote server returned an error: NotFound”等,返回的http狀態代碼為500。為尋找問題根源,我嘗試了以下方法:

1、懷疑Silverlight的C#代碼沒有將strFileContent傳遞給JavaScript
    在測試過程中,發現10~30M的檔案上傳沒有問題,但當檔案超過40M的時候就失敗。跟蹤調試後,上傳40M的檔案時在c#裡嘗試查看strFileContent的內容,但顯示“Unable to evaluate the expression. 儲存空間不足,無法完成此操作”。可能是因為strFileContent的長度太長,記憶體有限而無法正常顯示。會不會因此而使得strFileContent不能正確傳遞給JavaScript導致上傳失敗?我在JavaScript的AddAttachment函數開始時就加入顯示strFileContent長度的調試語句,結果顯示JavaScript已經獲得了完整的檔案內容,所以可以排出這個可能。實際上string是沒有長度限制的,大小取決於記憶體,至少到幾個G是沒問題的。

2、懷疑JavaScript受記憶體限制不能上傳大檔案,嘗試直接在Silverlight裡用C#的webclient直接調用SharePoint的Web Service上傳檔案
    那是不是JavaScript因為受某種限制(例如記憶體)而導致不能上傳大檔案的呢? 為此,我改變用JavaScript調用webservice的方式,而在c#中直接用webclient調用webservice上傳附件。

  1. string xmlrequest = "<?xml version=/"1.0/" encoding=/"utf-8/"?><soap:Envelope xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=/"http://www.w3.org/2001/XMLSchema/" xmlns:soap=/"http://schemas.xmlsoap.org/soap/envelope//"><soap:Body>" + "<AddAttachment xmlns=/"http://schemas.microsoft.com/sharepoint/soap//"><listName>FileList</listName><listItemID>4</listItemID><fileName>testFileName</fileName><attachment>" + strFileContent + "</attachment></AddAttachment>"; 
  2.             xmlrequest += "</soap:Body></soap:Envelope>"; 
  3.   
  4.             WebClient wc = new WebClient(); 
  5.             wc.Headers["Content-Type"] = "text/xml; charset=utf-8"; 
  6.             wc.Headers["SOAPAction"] = "http://schemas.microsoft.com/sharepoint/soap/AddAttachment"; 
  7.             //wc.Headers["Content-Length"] = xmlrequest.Length.ToString(); 
  8.   
  9.             wc.UploadStringCompleted += new UploadStringCompletedEventHandler(wc_UploadStringCompleted); 
  10.             wc.UploadStringAsync(new Uri("http://localhost/_vti_bin/Lists.asmx?op=AddAttachment", UriKind.Absolute), xmlrequest);

      但問題依舊,上傳的檔案過大時就返回“The remote server returned an error: NotFound”。

    註:用Silver light的webclient調用WebService需要在SharePoint的網站目錄下存放“clientaccesspolicy.xml”檔案,否則訪問會被拒絕。如果用JavaScript訪問webservice,則不須配置策略檔案,但也要求和sharePoint在同域下。

3、 懷疑是SharePoint的問題,嘗試直接在SharePoint裏手工操作上傳大附件
   
是不是SharePoint本身的問題呢?我嘗試直接在SharePoint裏手工操作上傳40M的檔案,沒有問題,上傳成功了。甚至稍大點檔案都沒問題(50M以下)。莫非是訪問Web Service的方式出問題了?

4、 嘗試在Silver light裡用C#直接引用SharePoint的Web Service( Add Service reference)並調用其方法上傳附件
    我改變一下調用webservice的方式,不用JavaScript的xmlhttp也不用Silverlight的webclient,而直接在silverlight project裡Add Service reference組建代理程式,直接用代理來調用webservice: 

  1. SRLists.ListsSoapClient lsc = new UploadFileToSharePointLists.SRLists.ListsSoapClient(); 
  2. lsc.AddAttachmentCompleted += new EventHandler<UploadFileToSharePointLists.SRLists.AddAttachmentCompletedEventArgs>(lsc_AddAttachmentCompleted); 
  3. lsc.AddAttachmentAsync("FileList", "4", "testFileName002", buffer);

       依然不能上傳40M的檔案。

5、 懷疑不是SharePoint的問題,我自己自訂一個Web Service讓silverlight來調用
    至此,可能不是SharePoint的問題了,我們得把SharePoint拋開,估計是WebService的問題。因為我們只是把webservice請求以某個報文格式發送給伺服器而已,一般只要伺服器端接收到資料請求,即時處理失敗,也不應該返回“未找到伺服器”的錯誤。可能我們的請求伺服器都沒有正確接收到。
        為此,我自己寫個WebService取代SharePoint的AddAttachment方法,而這個方法僅僅接收請求並只返迴文件的大小,不進行任何實際的檔案儲存操作。

  1.    [WebMethod] 
  2.     public string AddAttachment(byte[] buffer) 
  3.     { 
  4.               return buffer.Length.ToString();        
  5.    } 

    同樣,上傳小檔案時沒問題,檔案大了就失敗。但至少證明了這個問題和SharePoint沒有實質的關係,頂多是Web Service層面的問題。

6、 終於查到與web.config中的maxRequestLenght配置有關,但上傳檔案小於配置指定大小時還失敗
    這引起了我進一步的思考,WebService的發送請求對資料量有限制嗎?如果參數很多,資料量很大,怎麼把這些請求發送到伺服器端?我們的問題實質也是調用Web Service時參數資料量過大的問題。

    這讓我想起了Asp和.net裡上傳檔案時有大小限制的問題,實際上我早應該想起這個,只是以前對這個認識得不是非常清晰而已。從伺服器的角度來說,用戶端的請求都是一堆http資料包,有httpheader和http body之類的(對於webservice這麼說不嚴謹),伺服器是需要限制請求的http包的大小的,否則容易遭到攻擊。而web.config裡就有這個配置項:

  1. <configuration> 
  2.     <system.web> 
  3.           <httpRuntime maxRequestLength="1048576" executionTimeout="3600" /> 
  4.     </system.web> 
  5.  </configuration>  

    其中的maxRequestLength屬性就限制了請求資料包的最大長度。我嘗試更改我的web.config裡maxRequestLength的值,當這個值很大時,確實能上傳40M的資料。問題有點眉目了,接著尋找SharePoint網站下對應的web.config中該節配置屬性,發現maxRequestLength51200,剛好是50M。但為什麼我上傳40M的資料時就不行呢? 
    
  註:
maxRequestLength:指示 ASP.NET 支援的HTTP方式上傳的最大位元組數。該限制可用於防止因使用者將大量檔案傳遞到該伺服器而導致的拒絕服務的攻擊。指定的大小以 KB 為單位。預設值為 4096 KB (4 MB)。

      executionTimeout:指示在被 ASP.NET 自動關閉前,允許執行請求的最大秒數。還有其他相關屬性可以參考MSDN。該屬性的預設配置在Machine.config裡。       

       另外,伺服器端對記憶體使用量也是有限制的,可以參考以下配置節:
<configuration>
    <system.web>
         <processModel memoryLimit="80"  />
    </system.web>
</configuration>  
 
7、 基本鎖定是由web.config對上傳檔案大小限制造成的,所以在Asp.net裡用簡單的FileUpload來測試
    在上面已經基本確定了是maxRequestLength的設定問題,但當maxRequestLength設定成50M的時候,實際上傳不了50M,甚至40M都上傳不了。莫非這中間有誤差?誤差也太大了。於是就在Asp.Net裡測試,在asp.Net裡放一個FileUpload控制項,後台僅計算檔案的大小:
  .Aspx檔案內容:

  1. <asp:FileUpload ID="fu" runat="server"  />   
  2. <asp:Button ID="btn" runat="server" Text="Upload File" onclick="btn_Click" />

    .cs檔案: 

  1. protected void btn_Click(object sender, EventArgs e) 
  2.   int len = fu.PostedFile.ContentLength; 
  3. }

    確實,上傳檔案大小超過maxRequestLength時,出現“伺服器不可訪問”之類的錯誤,與前面的錯誤基本類似。如果將maxRequestLength配置為50M,也上傳不了50M,但可以上傳49.99M。誤差明顯減小了好多。

8、 用Fiddler來跟蹤資料包,真相大白
    為了徹底弄清楚上面的問題,我們最好能跟蹤到資料傳遞過程中的Http資料包的發送資訊,能看看到底發了多大的資料包。為此,我安裝Fiddler來進行測試。 

     1) 在asp.net裡用FileUpload上傳檔案時,傳遞的http資料包比選擇的檔案稍大。通過fiddler監測可以看到,發送的資料部分與選擇檔案大小一樣大,但http header多了viewstate與_evengName等內容(這些是Asp.Net機制所必需的,我寫得不準確),所以加起來比檔案大小稍微大一點點,這也是maxRequestLength設定為50M但實際傳遞的檔案只能49.99M的原因了。

     2) 在Silverlight裡調用WebService上傳檔案時,通過Fiddler監測,發現http請求的資料部分內容遠比實際檔案大小大。例如上傳20M的檔案,http請求包中body部分資料大小將近27M。我自己自訂一個文字檔,內容為“abcd123456”,上傳該檔案時發現http請求中檔案內容變成了“YQBiAGMAZAAxADIAMwA0ADUANgA=”,確實加大了檔案大小。在FileUpLoad中上傳時是明文的,調用WebService時怎麼改變了檔案內容呢?莫非加密了?後來才發現這不是加密,而是Convert.ToBase64String的結果。將byte[]的內容轉化成Base64String雖然變成字元了,但長度確實加大了很多。儘管有點的時候我們傳遞給webservice的參數直接是byte[],沒有顯式Convert.ToBase64String,但在傳遞過程中還是自動變成Base64String了,大大加大了傳遞的內容。

    至此,已經搞清楚為什麼SharePoint設定的上傳最大允許50M,但實際通過WebService上傳40M都不成功的原因了。 

 

************************************************************************************************************

關於Fiddler2:
       Fiddler是IE上監視http資料包的一個很好的調試外掛程式,但在使用過程中發現對localhost的請求它卻不理睬,通過網上尋找原來可以用以下方式來讓fiddler也攔截原生資料包:
       http://localhost:81/myweb        ----------------à         http://localhost.81/myWeb    (localhost後面緊接著加一個點號”.”,引用webservice也是如此)。
 
順便付上相關的一片短文:
============================================================================
IE7 and the .NET Framework are hardcoded not to send requests for Localhost through proxies.  Fiddler runs as a proxy.  The workaround is to use your machine name as the hostname instead of Localhost or 127.0.0.1.
So, for instance, rather than hitting http://localhost:8081/mytestpage.aspx, instead visit http://machinename:8081/mytestpage.aspx.  Alternatively, you can use http://localhost.:8081/mytestpage.aspx (note the trailing dot after localhost).  Alternatively, you can customize your Rules file like so:
static function OnBeforeRequest(oSession:Fiddler.Session){
  if (oSession.host.ToUpper() == "MYAPP") { oSession.host = "127.0.0.1:8081"; }
}
...and then navigate to http://myapp, which will act as an alias for 127.0.0.1:8081.
我比較喜歡localhost.  方式。
如果你使用IIS7的話,還可以自己定製
<system.net>
  <defaultProxy>
    <proxy  proxyaddress="http://127.0.0.1:8888" />
  </defaultProxy>
</system.net>
這樣,就會通過代理來訪問了,從而Fiddler就能捕獲到資料了。
 ************************************************************************************************************               
  
  最後來總結一下:
    1、問題的關鍵是maxRequestLength限制了http請求的最大資料大小, maxRequestLength指的是http請求所有資料總和(htt頭已經內容) ,而不是僅僅普通概念上的檔案; 
    2、Byte[]轉換成Base64String加大了資料量。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.