2017-02-22 5 views
1

Ich habe Anforderungen, um große XML-Dateien (zwischen 0,5 MB - 600 MB) zu laden, Daten innerhalb der XML-Datei zu entschlüsseln und diese zu schreiben zu einem MemoryStream.Wie man eine XML-Datei streamen (laden) kann, XML-Elementdaten ändern und in MemoryStream schreiben

Es ist wichtig, dass die entschlüsselten Daten nicht auf der Festplatte liegen.

Unten ist meine aktuelle Implementierung, die das gesamte XML-Dokument in den Speicher lädt, die Kartennummer entschlüsselt und den Wert festlegt und dann das geänderte XML-Dokument in eine MemoryStream kopiert. Diese Implementierung ist jedoch nicht durchführbar, da das gesamte XML-Dokument in den Arbeitsspeicher geladen wird.

public MemoryStream DecryptFile(string xmlFullPath, DateTime encryptionKey) 
{ 
    XNamespace xmlNameSpace = "http://www.xml.com/schema"; 

    XDocument fileXDocument = XDocument.Load(xmlFullPath); 

    IEnumerable<XElement> cardElements = 
     fileXDocument 
     .Descendants(xmlNameSpace + "card"); 

    // Iterate over each <card> element within the <batchRequest>. 
    foreach (XElement cardElement in cardElements) 
    { 
     XElement cardNumberElement = cardElement.Element(xmlNameSpace + "number"); 

     // Read encrypted value 
     // Decrypt value 

     cardNumberElement.SetValue(decryptedCreditCard); 
    } 

    // Save the XML document, with the decrypted cards, to a memory stream. 
    var memoryStream = new MemoryStream(); 

    fileXDocument.Save(memoryStream, SaveOptions.DisableFormatting); 

    // Rewind the stream, so that it's ready to be read from it elsewhere. 
    memoryStream.Position = 0; 

    return memoryStream; 
} 

Ich bin ziemlich vertraut mit den XmlReader, und ich verwende es für andere Operationen.

Ich habe darüber nachgedacht, in der Lage zu sein, das XML-Dokument zu durchlaufen und Element-für-Element einfach in einen entsprechenden MemoryStream zu schreiben und schließlich die Daten zu entschlüsseln und in den Speicherstrom zu schreiben, wenn die Kartennummern erscheinen.

Ich bin jedoch nicht in der Lage, das rohe XML des Start/End-Elements zu erhalten, das ich bearbeite. Zumindest ohne das gesamte Element zu parsen/laden, was eine Operation wie ReadOuterXml ermöglicht. Aber ich möchte nicht das ganze Element lesen. Ich möchte einfach das rohe Element nach Element in den MemoryStream schreiben und nur die Kartennummer entschlüsseln, wenn ich auf sie stoße.

Beachten Sie, dass sich die Kartennummern in einem serialisierten Objekt "Transaktion" befinden. <transaction>...<number>asdfa3423jasfa</number></transaction>

Also, wie kann ich eine XML-Datei laden (streamen), Bit-Daten darin ändern und den Inhalt progressiv in einen MemoryStream schreiben?

+0

Sie müssen dafür eine XML-Streaming-API verwenden. Versuchen Sie XmlTextReader. Sie wissen auch, dass MemoryStream auch alle gleichzeitig im Speicher ist, richtig? – hoodaticus

+1

@hoodaticus yea in Bezug auf den MemoryStream, unsere Testshow, die den MemoryStream direkt im RAM hält, ist nicht so schlecht wie das Laden der gesamten XML im RAM. Die Ladder verursacht Speichermangel-Ausnahmen, während der MemoryStream in unserer Umgebung und den Einschränkungen in Ordnung ist. – contactmatt

+0

Vergiss meinen XmlTextReader-Vorschlag nicht. Ich verwendete es für den Aufbau vollständig streambasierter Dienste - sehr schnelle und kleine, flache Speichernutzung war eine sehr gute Sache. – hoodaticus

Antwort

1

Sie das XmlReader mit lesen und schreiben alle Inhalte eine XmlWriter verwenden. Denken Sie daran, dass es sich um einen Nur-Vorwärts-Cursor handelt. Sie müssen also alles speichern, was Sie benötigen, während Sie es verarbeiten.

Unten ist eine Beispielfunktion, um etwas ähnliches zu tun, was Sie brauchen.

public static MemoryStream DecryptFile(string xmlFullPath, DateTime encryptionKey) { 
    var elemToLook = "number"; 
    var inElem = false; 
    var number = ""; 
    var memoryStream = new MemoryStream(); 
    using (var writer = XmlWriter.Create(memoryStream)) 
    using (var reader = XmlReader.Create(xmlFullPath)) { 
     while (reader.Read()) { 
      switch (reader.NodeType) { 
       case XmlNodeType.Element: 
        if (reader.Name == elemToLook) 
         inElem = true; 
        writer.WriteStartElement(reader.Name); 
        break; 
       case XmlNodeType.Text: 
        if (inElem) { 
         number = reader.Value; 
         // TODO: This is where your decryption code will go. 
         number = $"decrypted({number})"; 
         writer.WriteString(number); 
        } else 
         writer.WriteString(reader.Value); 
        break; 
       case XmlNodeType.XmlDeclaration: 
       case XmlNodeType.ProcessingInstruction: 
        writer.WriteProcessingInstruction(reader.Name, reader.Value); 
        break; 
       case XmlNodeType.Comment: 
        writer.WriteComment(reader.Value); 
        break; 
       case XmlNodeType.EndElement: 
        if (inElem) 
         inElem = false; 
        writer.WriteFullEndElement(); 
        break; 
       case XmlNodeType.Whitespace: 
        writer.WriteRaw(reader.Value); 
        break; 
      } 
     } 
    } 

    memoryStream.Position = 0; 
    return memoryStream; 
} 

Ich würde vorschlagen, dass Sie in einem Action Delegierten passieren die Verarbeitung zu tun, so dass Sie Ihre individuelle Verarbeitung mit dem Standard-XML-Verarbeitung

auch trennen können, wenn Sie nur die <number>... </number> lesen wollen, wenn es verschachtelt in einigen anderen Tags wie <transaction>...</transaction> dann müssen Sie damit umgehen, wenn Sie das inElem Flag einrichten, um die Verschachtelung einzubeziehen.

+0

Arbeitete wie ein Charme. Für meine geschäftlichen Anforderungen etwas modifiziert, aber das ist die richtige Antwort. – contactmatt

Verwandte Themen