2013-04-03 5 views
5

ENDE ZIEL: Effizientes (in einem Durchgang) Lesen Sie alle CellRecords auf einem riesigen (30.000+ Reihe), geschützt Worksheet.
lesen XLS mit geschütztem Buch und Blatt über HSSF.EventUserModel

Problem: Mit dem HSSF.EventUserModel, wie kann ich lesen Sie alle Record s (einschließlich CellRecords) für eine XLS-Datei sowohl mit der Arbeitsmappe und den Schutz Arbeitsblatt?

erstellen Eingabe Tabelle (in Excel 2010):

  1. Erstellen Sie neue leere Arbeitsmappe.
  2. Sollwert von A1 bis Nummer: 50
  3. Set Wert von A2 auf string: fünfzig
  4. Set Wert von A3 Formel: = 25 * 2
  5. Review (Band) -> Blatt schützen -> Passwort : pass1
  6. Review (Band) -> Arbeitsmappe schützen -> Passwort: pass1
  7. Datei (Band) -> Speichern unter ... -> Speichern unter: Excel 97-2003-Arbeitsmappe

Fortschritt bisher:

  • Die XLS-Datei wird ohne ein Kennwort in Excel geöffnet. Daher sollten Sie das Passwort nicht benötigen, um es in POI zu öffnen.
  • Die XLS-Datei wird erfolgreich mit new HSSFWorkbook(Stream fs) geöffnet. Allerdings benötige ich die Effizienz von EventUserModel für meine aktuelle Tabelle.
  • Einstellung NPOI.HSSF.Record.Crypto.Biff8EncryptionKey.CurrentUserPassword = "pass1"; hat nicht funktioniert.
  • Die ProcessRecord() Funktion fängt eine PasswordRecord, aber ich kann keine Dokumentation finden, wie man es richtig behandelt.
  • Vielleicht können die Klassen EncryptionInfo oder Decryptor von Nutzen sein.
  • Hinweis:
    Ich benutze NPOI. Ich kann jedoch jedes Java-Beispiel in C# übersetzen.

    Code:
    Ich verwende den folgenden Code, um Record Ereignisse zu erfassen. My Book1-unprotected.xls (ohne Schutz) zeigt alle Record Ereignisse (einschließlich Zellenwerte). Mein Book1-protected.xls zeigt einige Datensätze an und löst eine Ausnahme aus.

    Ich sehe gerade processedEvents im Debugger.

    using System; 
    using System.Collections.Generic; 
    using System.IO; 
    
    using NPOI.HSSF.Record; 
    using NPOI.HSSF.Model; 
    using NPOI.HSSF.UserModel; 
    using NPOI.HSSF.EventUserModel; 
    using NPOI.POIFS; 
    using NPOI.POIFS.FileSystem; 
    
    namespace NPOI_small { 
        class myListener : IHSSFListener { 
         List<Record> processedRecords; 
    
         private Stream fs; 
    
         public myListener(Stream fs) { 
          processedRecords = new List<Record>(); 
          this.fs = fs; 
    
          HSSFEventFactory factory = new HSSFEventFactory(); 
          HSSFRequest request = new HSSFRequest(); 
    
          MissingRecordAwareHSSFListener mraListener; 
          FormatTrackingHSSFListener fmtListener; 
          EventWorkbookBuilder.SheetRecordCollectingListener recListener; 
          mraListener = new MissingRecordAwareHSSFListener(this); 
          fmtListener = new FormatTrackingHSSFListener(mraListener); 
          recListener = new EventWorkbookBuilder.SheetRecordCollectingListener(fmtListener); 
          request.AddListenerForAllRecords(recListener); 
    
          POIFSFileSystem poifs = new POIFSFileSystem(this.fs); 
    
          factory.ProcessWorkbookEvents(request, poifs); 
         } 
    
         public void ProcessRecord(Record record) { 
          processedRecords.Add(record); 
         } 
        } 
        class Program { 
         static void Main(string[] args) { 
          Stream fs = File.OpenRead(@"c:\users\me\desktop\xx\Book1-protected.xls"); 
    
          myListener testListener = new myListener(fs); // Use EventModel 
          //HSSFWorkbook book = new HSSFWorkbook(fs); // Use UserModel 
    
          Console.Read(); 
         } 
        } 
    } 
    

    UPDATE (für Juan Mellado) : Unten ist die Ausnahme. Meine beste Schätzung im Moment (in der Antwort von Victor Petrykin) ist, dass die HSSFEventFactoryRecordInputStream verwendet, die geschützte Datensätze nicht nativ entschlüsseln kann.Auf die Ausnahme empfängt, processedRecords 22 Datensätze enthält, einschließlich der folgenden potenziell bedeutende diejenigen:

    • processedRecords [5] ist ein WriteAccessRecord mit einem verstümmelten (wahrscheinlich verschlüsselt) Wert für .name
    • processedRecords [22] ist ein RefreshAllRecord und in der Liste ist der letzte Record

    Ausnahme:

    NPOI.Util.RecordFormatException was unhandled 
        HResult=-2146233088 
        Message=Unable to construct record instance 
        Source=NPOI 
        StackTrace: 
         at NPOI.HSSF.Record.RecordFactory.ReflectionConstructorRecordCreator.Create(RecordInputStream in1) 
         at NPOI.HSSF.Record.RecordFactory.CreateSingleRecord(RecordInputStream in1) 
         at NPOI.HSSF.Record.RecordFactory.CreateRecord(RecordInputStream in1) 
         at NPOI.HSSF.EventUserModel.HSSFRecordStream.GetNextRecord() 
         at NPOI.HSSF.EventUserModel.HSSFRecordStream.NextRecord() 
         at NPOI.HSSF.EventUserModel.HSSFEventFactory.GenericProcessEvents(HSSFRequest req, RecordInputStream in1) 
         at NPOI.HSSF.EventUserModel.HSSFEventFactory.ProcessEvents(HSSFRequest req, Stream in1) 
         at NPOI.HSSF.EventUserModel.HSSFEventFactory.ProcessWorkbookEvents(HSSFRequest req, POIFSFileSystem fs) 
         at NPOI_small.myListener..ctor(Stream fs) in c:\Users\me\Documents\Visual Studio 2012\Projects\myTest\NPOI_small\Program.cs:line 35 
         at NPOI_small.Program.Main(String[] args) in c:\Users\me\Documents\Visual Studio 2012\Projects\myTest\NPOI_small\Program.cs:line 80 
         at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
         at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
         at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
         at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
         at System.Threading.ThreadHelper.ThreadStart() 
        InnerException: NPOI.Util.RecordFormatException 
         HResult=-2146233088 
         Message=Expected to find a ContinueRecord in order to read remaining 137 of 144 chars 
         Source=NPOI 
         StackTrace: 
          at NPOI.HSSF.Record.RecordInputStream.ReadStringCommon(Int32 requestedLength, Boolean pIsCompressedEncoding) 
          at NPOI.HSSF.Record.RecordInputStream.ReadUnicodeLEString(Int32 requestedLength) 
          at NPOI.HSSF.Record.FontRecord..ctor(RecordInputStream in1) 
    
    +0

    Ich habe eine Java-gleichwertige Version Ihres Codes getestet, und es funktioniert wie erwartet. Ich meine, es ist nicht notwendig, ein Passwort zum Lesen geschützter Zellen anzugeben. Die Callback-Methode 'processRecord' empfängt alle' CellRecord'-Objekte wie 'NumberRecord' oder' FormulaRecord', sodass ich sie erkennen und auf ihre Attribute zugreifen kann (Beispiel: '((NumberRecord) record) .getValue()'). Ein 'ProtectRecord'-Objekt hat nur ein Flag-Attribut, um zu sagen, ob der aktuelle Datensatz geschützt ist. Kannst du etwas mehr darüber sagen, was "wie man damit richtig umgehen kann" bedeutet? Und natürlich die Stapelverfolgung der Ausnahme, die Sie erhalten haben. –

    +0

    @JuanMellado: Siehe Hauptpostaktualisierung für Details. – Steven

    Antwort

    3

    Ich denke, es ist der Fehler in der NPOI Bibliothekscode. Soweit ich verstanden sie Art falsche Strom für HSSFEventFactory verwenden: verwendet es RecordInputStream statt RecordFactoryInputStream mit Entschlüsselungsfunktion wie in der ursprünglichen POI Bibliothek oder im UserModel (deshalb HSSFWorkbook arbeitet)

    Dieser Code zu arbeiten aber es ist keine Ereignislogik:

    POIFSFileSystem poifs = new POIFSFileSystem(fs); 
    Entry document = poifs.Root.GetEntry("Workbook"); 
    DocumentInputStream docStream = new DocumentInputStream((DocumentEntry)document); 
    //RecordFactory factory = new RecordFactory(); 
    //List<Record> records = RecordFactory.CreateRecords(docStream); 
    RecordFactoryInputStream recFacStream = new RecordFactoryInputStream(docStream, true); 
    Record currRecord; 
    while ((currRecord = recFacStream.NextRecord()) != null) 
        ProcessRecord(currRecord); 
    
    +0

    Das ist vielversprechend! Ich sehe jetzt alle Ereignisse für das 'Arbeitsbuch'. Wie bekomme ich Datensätze innerhalb eines Arbeitsblattes, namentlich "NumberRecord" (50), "LabelSSTRecord" ("fünfzig") und "StringRecord" (= 25 * 2)? – Steven

    +0

    Endlich !! Ein 'RecordFactory.CreateRecords (Stream in1)' ignoriert jeden verschlüsselten 'Record', anstatt einen Fehler zu werfen. Wie Sie gesagt haben, enthält ein 'RecordFactoryInputStream' eine eingebaute Entschlüsselungsfunktion. Sehen Sie den aktualisierten Code in Ihrer Antwort. Vielen Dank! – Steven