27 private readonly
string filePath;
28 private readonly Stream inputStream;
29 private readonly ReaderOptions readerOptions;
30 private MemoryStream memoryStream;
46 public XlsxReader(
string path, ReaderOptions options =
null)
49 readerOptions = options;
57 public XlsxReader(Stream stream, ReaderOptions options =
null)
60 readerOptions = options;
76 using (memoryStream =
new MemoryStream())
78 Task.Run(() => ReadInternal()).GetAwaiter().GetResult();
81 catch (NotSupportedContentException)
91 throw new IOException(
"There was an error while reading an XLSX file. Please see the inner exception:", ex);
106 using (memoryStream =
new MemoryStream())
108 await ReadInternal();
117 throw new IOException(
"There was an error while reading an XLSX file. Please see the inner exception:", ex);
125 private async Task ReadInternal()
128 if (inputStream ==
null && !
string.IsNullOrEmpty(filePath))
130 using (FileStream fs =
new FileStream(filePath, FileMode.Open))
132 await fs.CopyToAsync(memoryStream);
135 else if (inputStream !=
null)
139 await inputStream.CopyToAsync(memoryStream);
144 throw new IOException(
"No valid stream or file path was provided to open");
147 memoryStream.Position = 0;
148 zf =
new ZipArchive(memoryStream, ZipArchiveMode.Read);
153 }).ConfigureAwait(
false);
160 private void ReadZip(ZipArchive zf)
165 importInProgress =
true
167 Dictionary<string, ZipArchiveEntry> entryLookup =
new Dictionary<string, ZipArchiveEntry>(zf.Entries.Count, StringComparer.Ordinal);
168 foreach (ZipArchiveEntry entry
in zf.Entries)
170 entryLookup[entry.FullName] = entry;
173 IDiscoveryReader discoveryReader = PlugInLoader.GetPlugIn<IDiscoveryReader>(PlugInUUID.DiscoveryReader,
new DiscoveryReader());
174 discoveryReader.Init(zf, wb, readerOptions);
175 discoveryReader.Execute();
176 RelationshipCatalog relationshipCatalog = wb.AuxiliaryData.GetData<RelationshipCatalog>(PlugInUUID.DiscoveryReader, PlugInUUID.DiscoveryCatalogEntity);
177 if (relationshipCatalog ==
null)
179 throw new IOException(
"The relationship discovery reader did not provide a relationship catalog. The XLSX file may be corrupted.");
182 HandleQueuePlugIns(PlugInUUID.ReaderPackageRegistryQueue, entryLookup, relationshipCatalog, ref wb);
183 HandleQueuePlugIns(PlugInUUID.ReaderPrependingQueue, entryLookup, relationshipCatalog, ref wb);
185 IPluginBaseReader workbookReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.WorkbookReader,
new WorkbookReader());
186 RelationshipInfo workbookRelationship = GetRelationship(relationshipCatalog,
string.Empty, GetDocumentType(workbookReader,
new WorkbookReader().DocumentType),
true);
187 string workbookPartPath = workbookRelationship.ResolvedTargetPath;
189 ISharedStringReader sharedStringsReader = PlugInLoader.GetPlugIn<ISharedStringReader>(PlugInUUID.SharedStringsReader,
new SharedStringsReader());
190 RelationshipInfo sharedStringsRelationship = GetRelationship(relationshipCatalog, workbookPartPath, sharedStringsReader.DocumentType,
false);
191 if (sharedStringsRelationship !=
null)
193 ZipArchiveEntry sharedStringsEntry = GetRequiredEntry(sharedStringsRelationship, entryLookup);
194 if (PlugInLoader.HasQueuePlugins(PlugInUUID.SharedStringsInlineReader))
197 MemoryStream ssMs = GetEntryStream(sharedStringsRelationship.ResolvedTargetPath, entryLookup);
198 sharedStringsReader.Init(ssMs, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
199 sharedStringsReader.Execute();
204 using (Stream sharedStringsStream = sharedStringsEntry.Open())
206 sharedStringsReader.Init(sharedStringsStream, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
207 sharedStringsReader.Execute();
212 IPluginBaseReader themeReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.ThemeReader,
new ThemeReader());
216 RelationshipInfo themeRelationship = GetRelationship(relationshipCatalog, workbookPartPath, GetDocumentType(themeReader,
new ThemeReader().DocumentType),
false);
217 if (themeRelationship !=
null)
219 ms = GetRequiredEntryStream(themeRelationship, entryLookup);
220 themeReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
221 themeReader.Execute();
224 StyleRepository.Instance.ImportInProgress =
true;
225 IPluginBaseReader styleReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.StyleReader,
new StyleReader());
226 RelationshipInfo styleRelationship = GetRelationship(relationshipCatalog, workbookPartPath, GetDocumentType(styleReader,
new StyleReader().DocumentType),
true);
227 ms = GetRequiredEntryStream(styleRelationship, entryLookup);
228 styleReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
229 styleReader.Execute();
230 StyleRepository.Instance.ImportInProgress =
false;
232 ms = GetRequiredEntryStream(workbookRelationship, entryLookup);
233 workbookReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
234 workbookReader.Execute();
236 IPluginBaseReader metadataAppReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.MetadataAppReader,
new MetadataAppReader());
237 RelationshipInfo metadataAppRelationship = GetRelationship(relationshipCatalog,
string.Empty, GetDocumentType(metadataAppReader,
new MetadataAppReader().DocumentType),
false);
238 if (metadataAppRelationship !=
null)
240 ms = GetRequiredEntryStream(metadataAppRelationship, entryLookup);
241 metadataAppReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
242 metadataAppReader.Execute();
245 IPluginBaseReader metadataCoreReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.MetadataCoreReader,
new MetadataCoreReader());
246 RelationshipInfo metadataCoreRelationship = GetRelationship(relationshipCatalog,
string.Empty, GetDocumentType(metadataCoreReader,
new MetadataCoreReader().DocumentType),
false);
247 if (metadataCoreRelationship !=
null)
249 ms = GetRequiredEntryStream(metadataCoreRelationship, entryLookup);
250 metadataCoreReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
251 metadataCoreReader.Execute();
254 IPluginBaseReader relationships = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.RelationshipReader,
new RelationshipReader());
255 ms = GetEntryStream(GetRelationshipPartPath(workbookPartPath), entryLookup);
256 relationships.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
257 relationships.Execute();
259 IWorksheetReader worksheetReader = PlugInLoader.GetPlugIn<IWorksheetReader>(PlugInUUID.WorksheetReader,
new WorksheetReader());
260 worksheetReader.SharedStrings = sharedStringsRelationship ==
null ? null : sharedStringsReader.SharedStrings;
261 int worksheetVisualIndex = 0;
262 WorksheetDefinition definition;
263 while ((definition = wb.AuxiliaryData.GetData<WorksheetDefinition>(PlugInUUID.WorkbookReader, PlugInUUID.WorksheetDefinitionEntity, worksheetVisualIndex)) !=
null)
265 RelationshipInfo relationship = relationshipCatalog.GetBySourceAndId(workbookPartPath, definition.RelId);
266 if (relationship ==
null)
268 throw new IOException(
"There was an error while reading an XLSX file. The relationship target of the worksheet with the RelID " + definition.RelId +
" was not found");
270 if (relationship.TargetMode != System.IO.Packaging.TargetMode.Internal
271 || !entryLookup.TryGetValue(relationship.ResolvedTargetPath, out ZipArchiveEntry worksheetEntry))
273 throw new IOException(
"There was an error while reading an XLSX file. The worksheet entry '" + relationship.ResolvedTargetPath +
"' was not found in the archive");
275 worksheetReader.CurrentWorksheetID = worksheetVisualIndex;
276 if (PlugInLoader.HasQueuePlugins(PlugInUUID.WorksheetInlineReader))
279 MemoryStream wsMs = GetEntryStream(relationship.ResolvedTargetPath, entryLookup);
280 worksheetReader.Init(wsMs, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
281 worksheetReader.Execute();
286 using (Stream worksheetStream = worksheetEntry.Open())
288 worksheetReader.Init(worksheetStream, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
289 worksheetReader.Execute();
292 worksheetVisualIndex++;
294 if (wb.Worksheets.Count == 0)
296 throw new IOException(
"No worksheet was found in the workbook");
298 HandleQueuePlugIns(PlugInUUID.ReaderAppendingQueue, entryLookup, relationshipCatalog, ref wb);
300 IPluginReadProcessor finalizingProcessor = PlugInLoader.GetPlugIn<IPluginReadProcessor>(PlugInUUID.FinalizingProcessor,
new FinalizingProcessor());
301 finalizingProcessor.Init(wb, readerOptions, ReaderPlugInHandler.HandleInlineQueueProcessorPlugins);
302 finalizingProcessor.Execute();
304 wb.importInProgress =
false;
305 wb.AuxiliaryData.ClearTemporaryData();
316 private static MemoryStream GetEntryStream(
string name, Dictionary<string, ZipArchiveEntry> entryLookup)
318 if (!entryLookup.TryGetValue(name, out ZipArchiveEntry entry))
322 int capacity = (int)Math.Min(entry.Length,
int.MaxValue);
323 MemoryStream ms =
new MemoryStream(capacity);
324 using (Stream src = entry.Open())
341 private static RelationshipInfo GetRelationship(RelationshipCatalog catalog,
string sourcePartPath,
string documentType,
bool required)
343 foreach (RelationshipInfo relationship
in catalog.GetByType(documentType))
345 if (relationship.TargetMode == System.IO.Packaging.TargetMode.Internal
346 &&
string.Equals(relationship.SourcePartPath, sourcePartPath, StringComparison.Ordinal))
353 string sourceDescription =
string.IsNullOrEmpty(sourcePartPath) ?
"the package root" :
"'" + sourcePartPath +
"'";
354 throw new IOException(
"The required relationship type '" + documentType +
"' was not found for " + sourceDescription);
366 private static ZipArchiveEntry GetRequiredEntry(RelationshipInfo relationship, Dictionary<string, ZipArchiveEntry> entryLookup)
370 if (
string.IsNullOrEmpty(relationship.ResolvedTargetPath)
371 || !entryLookup.TryGetValue(relationship.ResolvedTargetPath, out ZipArchiveEntry entry))
373 throw new IOException(
"The relationship target entry '" + relationship.ResolvedTargetPath +
"' was not found in the archive");
384 private static MemoryStream GetRequiredEntryStream(RelationshipInfo relationship, Dictionary<string, ZipArchiveEntry> entryLookup)
386 GetRequiredEntry(relationship, entryLookup);
387 return GetEntryStream(relationship.ResolvedTargetPath, entryLookup);
396 private static string GetDocumentType(IPluginBaseReader reader,
string defaultDocumentType)
398 IDocumentReader documentReader = reader as IDocumentReader;
399 return documentReader ==
null ||
string.IsNullOrEmpty(documentReader.DocumentType)
400 ? defaultDocumentType
401 : documentReader.DocumentType;
409 private static string GetRelationshipPartPath(
string sourcePartPath)
411 Uri sourcePartUri =
new Uri(
"/" + sourcePartPath, UriKind.Relative);
412 Uri relationshipPartUri = System.IO.Packaging.PackUriHelper.GetRelationshipPartUri(sourcePartUri);
413 return relationshipPartUri.OriginalString.TrimStart(
'/');
423 private void HandleQueuePlugIns(
string queueUuid, Dictionary<string, ZipArchiveEntry> entryLookup, RelationshipCatalog relationshipCatalog, ref
Workbook workbook)
425 string lastUuid =
null;
426 IPluginQueueReader queueReader;
430 queueReader = PlugInLoader.GetNextQueuePlugIn<IPluginQueueReader>(queueUuid, lastUuid, out currentUuid);
431 MemoryStream ms =
null;
432 if (queueReader !=
null)
434 IDiscoveryPackageReader discoveryPackageReader = queueReader as IDiscoveryPackageReader;
435 if (discoveryPackageReader !=
null)
437 foreach (RelationshipInfo relationship
in relationshipCatalog.GetByType(discoveryPackageReader.DocumentType))
439 if (relationship.TargetMode != System.IO.Packaging.TargetMode.Internal
440 ||
string.IsNullOrEmpty(relationship.ResolvedTargetPath)
441 || !entryLookup.ContainsKey(relationship.ResolvedTargetPath))
445 discoveryPackageReader.CurrentRelationship = relationship;
446 ms = GetEntryStream(relationship.ResolvedTargetPath, entryLookup);
447 discoveryPackageReader.Init(ms, workbook, this.readerOptions,
null);
448 discoveryPackageReader.Execute();
450 lastUuid = currentUuid;
453 if (queueReader is IPluginPackageReader)
455 string streamPartName = (queueReader as IPluginPackageReader).StreamEntryName;
456 if (!
string.IsNullOrEmpty(streamPartName))
458 ms = GetEntryStream(streamPartName, entryLookup);
461 lastUuid = currentUuid;
466 queueReader.Init(ms, workbook, this.readerOptions,
null);
467 queueReader.Execute();
468 lastUuid = currentUuid;
475 }
while (queueReader !=
null);
483 this.inputStream?.Dispose();
484 GC.SuppressFinalize(
this);