Mittwoch, 21. Januar 2009

"Insufficient memory to continue the execution of the program" und "System.OutOfMemoryException" bei Upload in eine Dokumentbibliothek

Im Moment arbeite ich an dem Projekt zur Migration der Daten aus einem Livelink- in ein SharePoint-System. Eine der Herausforderungen ist das Migrieren einer vorhandenen Dateistruktur - bestehend aus Foldern, Dokumenten (mit mehreren Versionen) und URLs - in eine Dokumentbibliothek.

Neben der Tatsache, dass SharePoint stark begrenzte Vorgaben was die Maximallänge von Datei- und Verzeichnisnamen sowie deren Syntax betrifft - dazu vielleicht in einem anderen Post einmal mehr -, gibt es auch das Problem, dass Dateien nicht beliebig groß sein dürfen.

In der Zentraladministration unter Application Management > Web Application General Settings die Einstellung Maximum Upload Size. Standardmäßig sollten dort 50 MB eingestellt sein.

Laut Microsoft kann man nun Dateien von einer Umgebung in eine Dokumentenbibliothek laden mittels diesen Codes:


FileStream fStream = File.OpenRead(srcUrl);
byte[] contents = new byte[fStream.Length];
fStream.Read(contents, 0, (int)fStream.Length);
fStream.Close();

EnsureParentFolder(site, destUrl);
site.Files.Add(destUrl, contents);


Das funktioniert auch wunderbar. Wird versucht, eine Datei hochzuladen, die größer ist als die eingestellte Maximum Upload Size wirft SharePoint eine Exception, die auch aussagt, dass das File Size Limit Exceeded ist.

Eine weitere Projektanforderung besagt, dass Dokumente bis 200 MB hochgeladen werden sollen. Also erhöhte ich den Wert in der ZA. Das funktionierte eine Zeit lang auch ganz gut aber irgendwann tauchten solche Fehlermeldungen auf:

Insufficient memory to continue the execution of the program.
at Microsoft.SharePoint.Library.SPRequestInternalClass.PutFile(String bstrUrl, String bstrWebRelativeUrl, Object varFile, PutFileOpt PutFileOpt, String bstrCreatedBy, String bstrModifiedBy, Int32 iCreatedByID, Int32 iModifiedByID, Object varTimeCreated, Object varTimeLastModified, Object varProperties, String bstrCheckinComment, UInt32& pdwVirusCheckStatus, String& pVirusCheckMessage)
at Microsoft.SharePoint.Library.SPRequest.PutFile(String bstrUrl, String bstrWebRelativeUrl, Object varFile, PutFileOpt PutFileOpt, String bstrCreatedBy, String bstrModifiedBy, Int32 iCreatedByID, Int32 iModifiedByID, Object varTimeCreated, Object varTimeLastModified, Object varProperties, String bstrCheckinComment, UInt32& pdwVirusCheckStatus, String& pVirusCheckMessage)
at Microsoft.SharePoint.SPFileCollection.AddInternal(String urlOfFile, Object file, PutFileOpt fileOpt, String createdBy, String modifiedBy, Int32 createdByID, Int32 modifiedByID, DateTime timeCreated, DateTime timeLastModified, Object varProperties, String checkInComment, SPVirusCheckStatus& virusCheckStatus, String& virusCheckMessage)
at Microsoft.SharePoint.SPFileCollection.Add(String urlOfFile, Byte[] file, SPUser createdBy, SPUser modifiedBy, DateTime timeCreated, DateTime timeLastModified)...


Und zwar genau in der Codezeile, in der der SPFileCollection mit der Methode .add die neue Datei hinzugefügt werden sollte.

Die Irritation war groß zumal laut der oben verlinkten MSDN-Seite und dem dort vorgegebenen Code Dateigrößen bis 2 GB möglich sein sollten.

Ein paar Zeilen tiefer in meinem Logfile fand sich auch diese Fehlermeldung:

Exception of type 'System.OutOfMemoryException' was thrown.

Und zwar in der Codezeile, in der das Bytearray deklariert wurde:

byte[] contents = new byte[fStream.Length];

Das Problem an dieser Stelle ist ausnahmsweise mal nicht SharePoint sondern das Betriebssystem, bzw. .net. Anscheinend sind Byte-Arrays jenseits der ~100 MB-Grenze zu groß für den Speicher sodass es zu den oben genannten Fehlermeldungen kommt.

Glücklicherweise, und das verschweigt die MSDN in ihrem Artikel voll und ganz, kann als zweiter Parameter in der SPFileCollection.add-Methode anstelle eines Byte-Arrays auch ein Stream benutzt werden:


FileStream fStream = File.OpenRead(fileToUpload);
spFile = spFileCollection.Add(newDocumentName, fStream, fileCreator, fileModifier, DateTime.Parse(fileCreated), DateTime.Parse(fileModified));
fStream.Close();


Durch diese Methode wird nicht nur der Speicher geschont sondern werden auch weniger Codezeilen benötigt.