2009-12-31 4 views
7

J'essaie de suivre la progression du chargement de gros fichiers XML (je ne suis pas le fournisseur de ces fichiers) dans dotnet (C#, framework 3.5 SP1): de 1 Mo à 300 Mo sur un partage de fichiers réseau.suivre le chargement des énormes fichiers XML

J'utilise un XmlReader à des fins de chargement au lieu de la méthode directe XmlDocument.Load pour accélérer le processus de chargement. En passant, je n'ai trouvé nulle part sur internet/document comment suivre cette progression de chargement: aucun délégué/événement ne semble exister. Est-il possible d'effectuer cette tâche? Avoir le genre de fonctionnalité pour l'objectif de sauvegarde XML pourrait être une bonne chose à avoir.

Merci

+0

Où chargez-vous ces fichiers, dans un DOM/Database ou autre chose? Êtes-vous en train de les lire et de les traiter nœud par nœud ou en les mettant en mémoire? – A9S6

+0

Je suppose que j'oublie de mettre quelques informations: Je charge ces fichiers XML pour une API (j'ai source, mais je préfère ne pas éditer la logique/analyse) qui analysent le fichier (principalement avec Xpath). Cette API accepte dans XML param un chemin vers un fichier XML (et utilise XmlReader) ou un Stream. Je ne m'intéresse pas vraiment au processus d'analyse qui est rapide, en me concentrant uniquement sur le chargement en mémoire. – camous

+0

XmlReader ne prend pas en charge XPath, et fournit uniquement un accès série ... si vous avez besoin d'un traitement plus sophistiqué, vous pouvez utiliser XPathNaviator, ou si l'utilisation de la mémoire est importante, essayez vtd-xml –

Répondre

13

En supposant que vous lisez à partir d'un flux ici est un exemple (non parfait) de la façon de le faire ... Fondamentalement, le ProgressStreamWrapper encapsule le flux de fichier et déclenche un événement chaque fois que la position est modifiée.

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Reading big file..."); 

     FileStream fileStream = File.OpenRead("c:\\temp\\bigfile.xml"); 
     ProgressStreamWrapper progressStreamWrapper = new ProgressStreamWrapper(fileStream); 
     progressStreamWrapper.PositionChanged += (o, ea) => Console.WriteLine((double) progressStreamWrapper.Position/progressStreamWrapper.Length * 100 + "% complete"); 
     XmlReader xmlReader = XmlReader.Create(progressStreamWrapper); 

     while (xmlReader.Read()) 
     { 
      //read the xml document 
     } 

     Console.WriteLine("DONE"); 
     Console.ReadLine(); 
    } 
} 


public class ProgressStreamWrapper : Stream, IDisposable 
{ 
    public ProgressStreamWrapper(Stream innerStream) 
    { 
     InnerStream = innerStream; 
    } 

    public Stream InnerStream { get; private set; } 

    public override void Close() 
    { 
     InnerStream.Close(); 
    } 

    void IDisposable.Dispose() 
    { 
     base.Dispose(); 
     InnerStream.Dispose(); 
    } 

    public override void Flush() 
    { 
     InnerStream.Flush(); 
    } 

    public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) 
    { 
     return InnerStream.BeginRead(buffer, offset, count, callback, state); 
    } 

    public override int EndRead(IAsyncResult asyncResult) 
    { 
     int endRead = InnerStream.EndRead(asyncResult); 
     OnPositionChanged(); 
     return endRead; 
    } 

    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) 
    { 
     return InnerStream.BeginWrite(buffer, offset, count, callback, state); 
    } 

    public override void EndWrite(IAsyncResult asyncResult) 
    { 
     InnerStream.EndWrite(asyncResult); 
     OnPositionChanged(); ; 
    } 

    public override long Seek(long offset, SeekOrigin origin) 
    { 
     long seek = InnerStream.Seek(offset, origin); 
     OnPositionChanged(); 
     return seek; 
    } 

    public override void SetLength(long value) 
    { 
     InnerStream.SetLength(value); 
    } 

    public override int Read(byte[] buffer, int offset, int count) 
    { 
     int read = InnerStream.Read(buffer, offset, count); 
     OnPositionChanged(); 
     return read; 
    } 

    public override int ReadByte() 
    { 
     int readByte = InnerStream.ReadByte(); 
     OnPositionChanged(); 
     return readByte; 
    } 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
     InnerStream.Write(buffer, offset, count); 
     OnPositionChanged(); 
    } 

    public override void WriteByte(byte value) 
    { 
     InnerStream.WriteByte(value); 
     OnPositionChanged(); 
    } 

    public override bool CanRead 
    { 
     get { return InnerStream.CanRead; } 
    } 

    public override bool CanSeek 
    { 
     get { return InnerStream.CanSeek; } 
    } 

    public override bool CanTimeout 
    { 
     get { return InnerStream.CanTimeout; } 
    } 

    public override bool CanWrite 
    { 
     get { return InnerStream.CanWrite; } 
    } 

    public override long Length 
    { 
     get { return InnerStream.Length; } 
    } 

    public override long Position 
    { 
     get { return InnerStream.Position; } 
     set 
     { 
      InnerStream.Position = value; 
      OnPositionChanged(); 
     } 
    } 

    public event EventHandler PositionChanged; 

    protected virtual void OnPositionChanged() 
    { 
     if (PositionChanged != null) 
     { 
      PositionChanged(this, EventArgs.Empty); 
     } 
    } 

    public override int ReadTimeout 
    { 
     get { return InnerStream.ReadTimeout; } 
     set { InnerStream.ReadTimeout = value; } 
    } 

    public override int WriteTimeout 
    { 
     get { return InnerStream.WriteTimeout; } 
     set { InnerStream.WriteTimeout = value; } 
    } 
} 
+0

Oui, je pourrais fournir comme API param un Stream donc je regarderai ce genre de solution (événement PositionChanged). Mettra à jour aujourd'hui. – camous

+0

Je pense que nous avions la même idée, mais votre code était ici en premier, donc un +1 ;-p –

-1

Comment l'utilisation DataSet.Read()? Avec les chargeurs encastrables il n'y a pas beaucoup

ou,

// Create the document. 
     XmlDocument doc = new XmlDocument(); 
     doc.Load(file); 

     // Loop through all the nodes, and create the list of Product objects . 
     List<Product> products = new List<Product>(); 

     foreach (XmlElement element in doc.DocumentElement.ChildNodes) 
     { 
      Product newProduct = new Product(); 
      newProduct.ID = Int32.Parse(element.GetAttribute("ID")); 
      newProduct.Name = element.GetAttribute("Name"); 

      // If there were more than one child node, you would probably use 
      // another For Each loop here and move through the 
      // Element.ChildNodes collection. 
      newProduct.Price = Decimal.Parse(element.ChildNodes[0].InnerText); 

      products.Add(newProduct); 
     } 
+0

Fondamentalement, j'essaie de me concentrer sur le mécanisme de chargement et non sur celui d'analyse: le processus d'analyse est fait par une API externe. Dans votre exemple, 'doc.Load (fichier);' chargera le fichier XML global à cette étape et aller plus loin seulement quand le fichier sera chargé en mémoire. – camous

2

; vous pouvez, cependant, écrire un flux d'interception - charger votre document à partir de ce flux et exposer le Position via des événements? c'est-à-dire, déclencher un événement dans la méthode Read (à intervalles réguliers)?


Voici un exemple qui prend en charge les mises à jour au cours de lecture et d'écriture:

using System; 
using System.IO; 
using System.Xml; 
class ChattyStream : Stream 
{ 
    private Stream baseStream; 
    public ChattyStream(Stream baseStream) 
    { 
     if (baseStream == null) throw new ArgumentNullException("baseStream"); 
     this.baseStream = baseStream; 
     updateInterval = 1000; 
    } 
    public event EventHandler ProgressChanged; 
    protected virtual void OnProgressChanged() 
    { 
     var handler = ProgressChanged; 
     if (handler != null) handler(this, EventArgs.Empty); 
    } 
    private void CheckDisposed() 
    { 
     if (baseStream == null) throw new ObjectDisposedException(GetType().Name); 
    } 
    protected Stream BaseStream 
    { 
     get { CheckDisposed(); return baseStream; } 
    } 
    int pos, updateInterval; 
    public int UpdateInterval 
    { 
     get { return updateInterval; } 
     set 
     { 
      if (value <= 0) throw new ArgumentOutOfRangeException("value"); 
      updateInterval = value; 
     } 
    } 

    protected void Increment(int value) 
    { 
     if (value > 0) 
     { 
      pos += value; 
      if (pos >= updateInterval) 
      { 
       OnProgressChanged(); 
       pos = pos % updateInterval; 
      } 
     } 
    } 
    public override int Read(byte[] buffer, int offset, int count) 
    { 
     int result = BaseStream.Read(buffer, offset, count); 
     Increment(result); 
     return result; 
    } 
    public override void Write(byte[] buffer, int offset, int count) 
    { 
     BaseStream.Write(buffer, offset, count); 
     Increment(count); 
    } 
    public override void SetLength(long value) 
    { 
     BaseStream.SetLength(value); 
    } 
    public override void Flush() 
    { 
     BaseStream.Flush(); 
    } 
    public override long Position 
    { 
     get { return BaseStream.Position; } 
     set { BaseStream.Position = value; } 
    } 
    public override long Seek(long offset, SeekOrigin origin) 
    { 
     return BaseStream.Seek(offset, origin); 
    } 
    public override long Length 
    { 
     get { return BaseStream.Length; } 
    } 
    public override bool CanWrite 
    { 
     get { return BaseStream.CanWrite; } 
    } 
    public override bool CanRead 
    { 
     get { return BaseStream.CanRead; } 
    } 
    public override bool CanSeek 
    { 
     get { return BaseStream.CanSeek; } 
    } 
    protected override void Dispose(bool disposing) 
    { 
     if (disposing && baseStream != null) 
     { 
      baseStream.Dispose(); 
     } 
     baseStream = null; 
     base.Dispose(disposing); 
    } 
    public override void Close() 
    { 
     if (baseStream != null) baseStream.Close(); 
     base.Close(); 
    } 
    public override int ReadByte() 
    { 
     int val = BaseStream.ReadByte(); 
     if (val >= 0) Increment(1); 
     return val; 
    } 
    public override void WriteByte(byte value) 
    { 
     BaseStream.WriteByte(value); 
     Increment(1); 
    } 

} 
static class Program 
{ 
    static void Main() 
    { 
     /* invent some big data */ 
     const string path = "bigfile"; 
     if (File.Exists(path)) File.Delete(path); 
     using (var chatty = new ChattyStream(File.Create(path))) 
     { 
      chatty.ProgressChanged += delegate 
      { 
       Console.WriteLine("Writing: " + chatty.Position); 
      }; 
      using (var writer = XmlWriter.Create(chatty)) 
      { 
       writer.WriteStartDocument(); 
       writer.WriteStartElement("xml"); 
       for (int i = 0; i < 50000; i++) 
       { 
        writer.WriteElementString("add", i.ToString()); 
       } 
       writer.WriteEndElement(); 
       writer.WriteEndDocument(); 
      } 
      chatty.Close(); 
     } 


     /* read it */ 

     using (var chatty = new ChattyStream(File.OpenRead("bigfile"))) 
     { 
      chatty.ProgressChanged += delegate 
      { 
       Console.WriteLine("Reading: " + chatty.Position); 
      }; 

      // now read "chatty" with **any** API; XmlReader, XmlDocument, XDocument, etc 
      XmlDocument doc = new XmlDocument(); 
      doc.Load(chatty); 
     } 
    } 
} 
Questions connexes