2009-08-19 13 views
4

Je souhaite générer un fichier XML à partir d'un objet (contient une collection imbriquée) avec une grande quantité de données. mais il existe une limitation avec XML qu'il ne peut pas dépasser 50 Mo.Restriction ou limitation de taille de fichier en C#

Y a-t-il un bon moyen de le faire?

Mise à jour: vitesse n'a pas d'importance, le principal est divisé en 50 Mo pour chaque fichier

+0

Qu'allez-vous faire avec le reste? À quoi ressembleraient vos fichiers de sortie? –

+0

Quels sont les problèmes avec jusqu'à diviser en 50 fichiers? –

Répondre

1

Avez-vous envisagé ecrivais le fichier XML comme une chaîne au lieu d'utiliser le support de XML dans .NET. J'écrivais ~ 10 Go de données en XML, car c'était la seule façon dont un outil pouvait le consommer.

J'avais un problème comme celui-ci mais mon XML était si simple que j'ai juste utilisé un TextWriter et imbriqué pour des boucles pour écrire le XML.

Travaillé un charme, plus était beaucoup plus rapide que l'objet XML.

+0

Tout est plus rapide que l'objet xml;) – NotMe

2

Vous pouvez écrire gros fichier xml avec XmlWriter ou XDocument sans aucun problème.

Voici un exemple d'exemple. Cet exemple génère un fichier XML de 63 Mo en moins de 5 secondes. Pour cet exemple, j'utilise la classe XmlWriter.

using (XmlWriter writer = XmlWriter.Create("YourFilePath")) 
{ 
    writer.WriteStartDocument(); 

    writer.WriteStartElement("Root"); 

    for (int i = 0; i < 1000000; i++) //Write one million nodes. 
    { 
     writer.WriteStartElement("Root"); 
     writer.WriteAttributeString("value", "Value #" + i.ToString()); 
     writer.WriteString("Inner Text #" + i.ToString()); 
     writer.WriteEndElement(); 
    } 
    writer.WriteEndElement(); 

    writer.WriteEndDocument(); 
} 
+0

J'ai écrit/lu des fichiers xml de plusieurs gigaoctets en utilisant cette méthode, cela fonctionne très bien. Pour un crédit supplémentaire, vous pouvez le connecter via un GzipStream pour compresser le fichier ... –

2

Ran dans une exigence similaire dans mon travail. Mon meilleur effort (intuitif, facilité de mise en œuvre, relativement performant) est le suivant. J'écris essentiellement avec un XmlWriter, surveillant le flux sous-jacent. Quand il dépasse ma limite de taille de fichier, je complète le fragment Xml actuel, enregistre le fichier, ferme le flux.

Ensuite, lors d'un second passage, je charge le DOM complet en mémoire et je supprime itérativement les noeuds et enregistre le document jusqu'à ce qu'il soit de taille acceptable.

Par exemple

// arbitrary limit of 10MB 
long FileSizeLimit = 10*1024*1024; 

// open file stream to monitor file size 
using (FileStream file = new FileStream("some.data.xml", FileMode.Create)) 
using (XmlWriter writer = XmlWriter.Create(file)) 
{ 
    writer.WriteStartElement("root"); 

    // while not greater than FileSizeLimit 
    for (; file.Length < FileSizeLimit;) 
    { 
     // write contents 
     writer.WriteElementString(
      "data", 
      string.Format("{0}/{0}/{0}/{0}/{0}", Guid.NewGuid())); 
    } 

    // complete fragment; this is the trickiest part, 
    // since a complex document may have an arbitrarily 
    // long tail, and cannot be known during file size 
    // sampling above 
    writer.WriteEndElement(); 
    writer.Flush(); 
} 

// iteratively reduce document size 
// NOTE: XDocument will load full DOM into memory 
XDocument document = XDocument.Load("some.data.xml"); 
XElement root = document.Element("root"); 
for (; new FileInfo("some.data.xml").Length > FileSizeLimit;) 
{ 
    root.LastNode.Remove(); 
    document.Save("some.data.xml"); 
} 

Il existe des moyens d'améliorer ce; une possibilité si la mémoire est une contrainte serait de réécrire le bit itératif pour prendre le nombre de nœuds réellement écrits en premier passage, puis réécrire le fichier moins un élément, et continuer jusqu'à ce que le document entier ait la taille désirée.

Cette dernière recommandation peut être la route à suivre, en particulier si vous avez déjà besoin de suivre les éléments écrits pour reprendre l'écriture dans un autre fichier.

Espérons que cela aide!


EDIT

Bien intuitive et plus facile à mettre en œuvre, je l'ai senti la peine enquête sur l'optimisation mentionnée ci-dessus. C'est ce que j'ai.

Une méthode d'extension qui permet de noeuds ancêtres d'écriture (par exemple des noeuds de conteneurs, et tous les autres types de balisage),

// performs a shallow copy of a given node. courtesy of Mark Fussell 
// http://blogs.msdn.com/b/mfussell/archive/2005/02/12/371546.aspx 
public static void WriteShallowNode(this XmlWriter writer, XmlReader reader) 
{ 

    switch (reader.NodeType) 
    { 
     case XmlNodeType.Element: 
      writer.WriteStartElement(
       reader.Prefix, 
       reader.LocalName, 
       reader.NamespaceURI); 
      writer.WriteAttributes(reader, true); 
      if (reader.IsEmptyElement) 
      { 
       writer.WriteEndElement(); 
      } 
      break; 
     case XmlNodeType.Text: writer.WriteString(reader.Value); break; 
     case XmlNodeType.Whitespace: 
     case XmlNodeType.SignificantWhitespace: 
      writer.WriteWhitespace(reader.Value); 
      break; 
     case XmlNodeType.CDATA: writer.WriteCData(reader.Value); break; 
     case XmlNodeType.EntityReference: 
      writer.WriteEntityRef(reader.Name); 
      break; 
     case XmlNodeType.XmlDeclaration: 
     case XmlNodeType.ProcessingInstruction: 
      writer.WriteProcessingInstruction(reader.Name, reader.Value); 
      break; 
     case XmlNodeType.DocumentType: 
      writer.WriteDocType(
       reader.Name, 
       reader.GetAttribute("PUBLIC"), 
       reader.GetAttribute("SYSTEM"), 
       reader.Value); 
      break; 
     case XmlNodeType.Comment: writer.WriteComment(reader.Value); break; 
     case XmlNodeType.EndElement: writer.WriteFullEndElement(); break; 
    } 
} 

et un procédé qui va effectuer la coupe (pas une méthode d'extension, depuis étendant l'une quelconque des les types de paramètres seraient un peu ambigus).

// trims xml file to specified file size. does so by 
// counting number of "victim candidates" and then iteratively 
// trimming these candidates one at a time until resultant 
// file size is just less than desired limit. does not 
// consider nested victim candidates. 
public static void TrimXmlFile(string filename, long size, string trimNodeName) 
{ 
    long fileSize = new FileInfo(filename).Length; 
    long workNodeCount = 0; 

    // count number of victim elements in xml 
    if (fileSize > size) 
    { 
     XmlReader countReader = XmlReader.Create(filename); 
     for (; countReader.Read();) 
     { 
      if (countReader.NodeType == XmlNodeType.Element && 
       countReader.Name == trimNodeName) 
      { 
       workNodeCount++; 
       countReader.Skip(); 
      } 
     } 
     countReader.Close(); 
    } 

    // if greater than desired file size, and there is at least 
    // one victim candidate 
    string workFilename = filename+".work"; 
    for (; 
     fileSize > size && workNodeCount > 0; 
     fileSize = new FileInfo(filename).Length) 
    { 
     workNodeCount--; 
     using (FileStream readFile = new FileStream(filename, FileMode.Open)) 
     using (FileStream writeFile = new FileStream(
      workFilename, 
      FileMode.Create)) 
     { 
      XmlReader reader = XmlReader.Create(readFile); 
      XmlWriter writer = XmlWriter.Create(writeFile); 

      long j = 0; 
      bool hasAlreadyRead = false; 
      for (; (hasAlreadyRead) || reader.Read();) 
      { 

       // if node is a victim node 
       if (reader.NodeType == XmlNodeType.Element && 
        reader.Name == trimNodeName) 
       { 
        // if we have not surpassed this iteration's 
        // allowance, preserve node 
        if (j < workNodeCount) 
        { 
         writer.WriteNode(reader, true); 
        } 
        j++; 

        // if we have exceeded this iteration's 
        // allowance, trim node (and whitespace) 
        if (j >= workNodeCount) 
        { 
         reader.ReadToNextSibling(trimNodeName); 
        } 
        hasAlreadyRead = true; 
       } 
       else 
       { 
        // some other xml content we should preserve 
        writer.WriteShallowNode(reader); 
        hasAlreadyRead = false; 
       } 
      } 
      writer.Flush(); 
     } 
     File.Copy(workFilename, filename, true); 
    } 
    File.Delete(workFilename); 
} 

Si votre Xml contient la mise en forme des espaces, les espaces blancs entre le dernier nœud victime restant et balise de fermeture de l'élément conteneur est perdu. Cela peut être atténué en modifiant la clause skip (en déplaçant le saut de message d'instruction j++), mais vous vous retrouvez avec des espaces supplémentaires. La solution présentée ci-dessus génère une réplique de taille minimale du fichier source.