2013-04-03 13 views
5

FINE OBIETTIVO: efficiente (in un solo passaggio) Leggi tutte le CellRecords su un enorme (30.000 fila), protetto Worksheet.
Leggi XLS con protetta Libro e Foglio via HSSF.EventUserModel

Problema: Utilizzando il HSSF.EventUserModel, come posso leggere tutti Record s (tra cui CellRecords) per un file XLS sia con cartella di lavoro e la protezione del foglio di lavoro?

Crea foglio di calcolo di ingresso (in Excel 2010):

  1. Crea nuova cartella di lavoro vuoto.
  2. valore Set di A1 al numero di: 50
  3. valore Set di A2 a stringa: cinquanta
  4. valore Set di A3 la formula: = 25 * 2
  5. Review (ribbon) -> Proteggi foglio -> Password : pass1
  6. Review (ribbon) -> Proteggi cartella di lavoro -> password: pass1
  7. file (nastro) -> Salva con nome ... -> Salva come: Excel 97-2003

Progress Finora:

  • Il file XLS si apre senza una password in Excel. Pertanto, non dovresti aver bisogno della password per aprirla nel POI.
  • Il file XLS si apre correttamente con new HSSFWorkbook(Stream fs). Tuttavia, ho bisogno dell'efficienza di EventUserModel per il mio foglio di calcolo effettivo.
  • L'impostazione NPOI.HSSF.Record.Crypto.Biff8EncryptionKey.CurrentUserPassword = "pass1"; non ha funzionato.
  • La funzione ProcessRecord() rileva uno PasswordRecord, ma non riesco a trovare alcuna documentazione su come gestirlo correttamente.
  • Forse le classi EncryptionInfo o Decryptor possono essere di qualche utilità.

Nota:
Sto utilizzando NPOI. Tuttavia, posso tradurre qualsiasi esempio java in C#.

Codice:
Io uso il seguente codice per acquisire Record eventi. Il mio Book1-unprotected.xls (senza protezione) mostra tutti gli eventi Record (compresi i valori delle celle). Il mio Book1-protected.xls visualizza alcuni record e genera un'eccezione.

Ho appena visualizzato processedEvents nel 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 (per Juan Mellado) : Sotto è l'eccezione. La mia ipotesi migliore ora (nella risposta di Victor Petrykin) è che lo HSSFEventFactory utilizza RecordInputStream che non può decifrare in modo nativo i record protetti.Alla ricezione eccezione, processedRecords contiene 22 records compresi i seguenti potenzialmente significative:

  • processedRecords [5] è un WriteAccessRecord con un valore alterata (probabilmente criptato) per .name
  • processedRecords [22] è una RefreshAllRecord e è l'ultimo Record nella lista

Eccezione:

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

Ho testato una versione equivalente Java del codice e funziona come previsto. Voglio dire, non è necessario specificare alcuna password per leggere le celle protette. Il metodo di callback 'processRecord' riceve tutti gli oggetti' CellRecord', come 'NumberRecord' o' FormulaRecord', in modo che io possa rilevarli e accedere ai loro attributi (Ex: '((Record di NumberRecord)) .getValue()'). Un oggetto 'ProtectRecord' ha solo un attributo flag per dire se il record corrente è protetto. Puoi aggiungere qualche informazione in più su cosa significa "come gestirlo correttamente"? E, ovviamente, la traccia dello stack dell'eccezione che hai ottenuto. –

+0

@JuanMellado: vedere l'aggiornamento del post principale per i dettagli. – Steven

risposta

3

Penso che sia l'errore nel codice della libreria NPOI. Per quanto ho capito che utilizzano tipo di flusso corretto per HSSFEventFactory: utilizza RecordInputStream invece di RecordFactoryInputStream con funzione di decifratura come nell'originale POI biblioteca o nel UserModel (è per questo che HSSFWorkbook sta lavorando)

Questo codice funziona troppo ma non è una logica evento:

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

Questo è promettente! Ora vedo tutti gli eventi per il 'Workbook'. Come posso ottenere i record all'interno di un 'foglio di lavoro ?, in particolare' NumberRecord' (50), 'LabelSSTRecord' (" fifty ") e' StringRecord' (= 25 * 2)? – Steven

+0

Finalmente !! Un 'RecordFactory.CreateRecords (Stream in1)' ignora ogni 'Record' criptato piuttosto che lanciare un errore. Come hai affermato, un "RecordFactoryInputStream" include una funzione di decrittografia incorporata. Guarda il codice aggiornato nella tua risposta. Grazie! – Steven