2013-04-03 5 views
5

END cel: Skutecznie (w jednym przejściu) czytaj wszystkie CellRecords na ogromnym (30.000 rzędzie), chroniony Worksheet.
Czytaj XLS z zabezpieczonymi Książki i Karcie poprzez HSSF.EventUserModel

Problem: Używanie HSSF.EventUserModel, w jaki sposób można odczytać wszystkie Record s (w tym CellRecords) do pliku XLS z obu skoroszytu i ochrony Arkusz?

Tworzenie arkusza kalkulacyjnego Excel wejścia (w 2010 roku):

  1. Utwórz nowy pusty skoroszyt.
  2. wartość Zestaw A1 na numer: 50
  3. Ustawiona wartość A2 ciąg: pięćdziesiąt
  4. ustawionej wartości A3 do wzoru: = 25 * 2
  5. Review (wstążka) -> Chroń arkusz -> Hasło : pass1
  6. Review (wstążka) -> Chroń skoroszyt -> Hasło: pass1
  7. plików (wstążka) -> Zapisz jako ... -> Zapisz jako typ: Excel 97-2003 skoroszyt

Progress do tej pory:

  • Plik XLS otwiera się bez hasła w programie Excel. Dlatego nie trzeba hasła, aby otworzyć go w POI.
  • Plik XLS otwiera się pomyślnie pod numerem new HSSFWorkbook(Stream fs). Jednak potrzebuję wydajności EventUserModel dla mojego rzeczywistego arkusza kalkulacyjnego.
  • Ustawienie NPOI.HSSF.Record.Crypto.Biff8EncryptionKey.CurrentUserPassword = "pass1"; nie działa.
  • Funkcja przechwytuje numer PasswordRecord, ale nie mogę znaleźć żadnej dokumentacji dotyczącej prawidłowego posługiwania się nim.
  • Być może niektóre klasy mogą mieć klasę EncryptionInfo lub Decryptor.

Uwaga:
Używam NPOI. Mogę jednak przetłumaczyć dowolne przykłady języka Java na język C#.

Kod:
Poniższy kod służy do rejestrowania zdarzeń Record. Mój Book1-unprotected.xls (bez zabezpieczenia) pokazuje wszystkie zdarzenia (w tym wartości komórek). Mój Book1-protected.xls wyświetla niektóre rekordy i zgłasza wyjątek.

Właśnie widzę processedEvents w debugerze.

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 (Juan Mellado) : Poniżej jest wyjątkiem. Moje najlepsze przypuszczenie w tej chwili (w odpowiedzi Victora Petrykina) jest takie, że HSSFEventFactory używa RecordInputStream, która nie może natywnie odszyfrować chronionych rekordów.Po otrzymaniu wyjątek processedRecords zawiera 22 rejestrów w tym następujących, potencjalnie istotne, takie:

  • processedRecords [5] jest WriteAccessRecord z zniekształcone (prawdopodobnie zaszyfrowanych) wartości dla .name
  • processedRecords [22] jest RefreshAllRecord i jest to ostatnia Record na liście

Wyjątek:

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

Przetestowałem wersję kodu równoważną Java i działa ona zgodnie z oczekiwaniami. To znaczy, nie trzeba określać żadnego hasła do odczytu chronionych komórek. Metoda zwrotna 'processRecord' odbiera wszystkie obiekty' CellRecord', takie jak 'NumberRecord' lub' FormulaRecord', dzięki czemu mogłem je wykryć i uzyskać dostęp do ich atrybutów (Ex: '((NumberRecord)) .getValue()'). Obiekt 'ProtectRecord' ma atrybut flag, który określa, czy bieżący rekord jest chroniony. Czy możesz dodać więcej informacji o tym, co oznacza "jak prawidłowo sobie z tym poradzić"? I, oczywiście, ślad stosu wyjątku, jaki masz. –

+0

@JuanMellado: Szczegółowe informacje można znaleźć w głównej aktualizacji posta. – Steven

Odpowiedz

3

Myślę, że to błąd w kodzie biblioteki NPOI. O ile zrozumiałem używają niewłaściwy typ strumienia do HSSFEventFactory: używa RecordInputStream zamiast RecordFactoryInputStream z funkcji deszyfrowania jak w oryginalnym POI bibliotece lub w UserModel (dlatego HSSFWorkbook pracuje)

Ten kod działa zbyt ale to nie jest logika zdarzeń:

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

To obiecuje! Widzę teraz wszystkie zdarzenia w "Podręczniku". Jak uzyskać zapisy w "arkuszu roboczym", w szczególności 'NumberRecord' (50),' LabelSSTRecord' ("fifty") i 'StringRecord' (= 25 * 2)? – Steven

+0

Wreszcie !! 'RecordFactory.CreateRecords (Stream in1)' ignoruje każdy zaszyfrowany 'Record' zamiast zgłaszać błąd. Jak stwierdziłeś, 'RecordFactoryInputStream' zawiera wbudowaną funkcję odszyfrowywania. Zobacz zaktualizowany kod w swojej odpowiedzi. Dziękuję Ci! – Steven