29 private readonly
string filePath;
30 private readonly Stream inputStream;
31 private readonly ReaderOptions readerOptions;
32 private MemoryStream memoryStream;
48 public XlsxReader(
string path, ReaderOptions options =
null)
51 readerOptions = options;
59 public XlsxReader(Stream stream, ReaderOptions options =
null)
62 readerOptions = options;
78 using (memoryStream =
new MemoryStream())
80 Task.Run(() => ReadInternal()).GetAwaiter().GetResult();
83 catch (NotSupportedContentException)
93 throw new IOException(
"There was an error while reading an XLSX file. Please see the inner exception:", ex);
108 using (memoryStream =
new MemoryStream())
110 await ReadInternal();
119 throw new IOException(
"There was an error while reading an XLSX file. Please see the inner exception:", ex);
127 private async Task ReadInternal()
130 if (inputStream ==
null && !
string.IsNullOrEmpty(filePath))
132 using (FileStream fs =
new FileStream(filePath, FileMode.Open))
134 await fs.CopyToAsync(memoryStream);
137 else if (inputStream !=
null)
141 await inputStream.CopyToAsync(memoryStream);
146 throw new IOException(
"No valid stream or file path was provided to open");
149 memoryStream.Position = 0;
150 zf =
new ZipArchive(memoryStream, ZipArchiveMode.Read);
155 }).ConfigureAwait(
false);
162 private void ReadZip(ZipArchive zf)
167 importInProgress =
true
169 Dictionary<string, ZipArchiveEntry> entryLookup =
new Dictionary<string, ZipArchiveEntry>(zf.Entries.Count, StringComparer.Ordinal);
170 foreach (ZipArchiveEntry entry
in zf.Entries)
172 entryLookup[entry.FullName] = entry;
174 HandleQueuePlugIns(PlugInUUID.ReaderPrependingQueue, entryLookup, ref wb);
176 ISharedStringReader sharedStringsReader = PlugInLoader.GetPlugIn<ISharedStringReader>(PlugInUUID.SharedStringsReader,
new SharedStringsReader());
177 if (entryLookup.TryGetValue(
"xl/sharedStrings.xml", out ZipArchiveEntry sharedStringsEntry) && sharedStringsEntry.Length > 0)
179 if (PlugInLoader.HasQueuePlugins(PlugInUUID.SharedStringsInlineReader))
182 MemoryStream ssMs = GetEntryStream(
"xl/sharedStrings.xml", entryLookup);
183 sharedStringsReader.Init(ssMs, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
184 sharedStringsReader.Execute();
189 using (Stream sharedStringsStream = sharedStringsEntry.Open())
191 sharedStringsReader.Init(sharedStringsStream, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
192 sharedStringsReader.Execute();
196 Dictionary<int, string> themeStreamNames = GetSequentialStreamNames(
"xl/theme/theme", entryLookup);
197 if (themeStreamNames.Count > 0)
202 foreach (KeyValuePair<int, string> streamName
in themeStreamNames)
204 IPluginBaseReader themeReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.ThemeReader,
new ThemeReader());
205 ms = GetEntryStream(streamName.Value, entryLookup);
206 themeReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
207 themeReader.Execute();
211 StyleRepository.Instance.ImportInProgress =
true;
212 IPluginBaseReader styleReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.StyleReader,
new StyleReader());
213 ms = GetEntryStream(
"xl/styles.xml", entryLookup);
214 styleReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
215 styleReader.Execute();
216 StyleRepository.Instance.ImportInProgress =
false;
218 ms = GetEntryStream(
"xl/workbook.xml", entryLookup);
219 IPluginBaseReader workbookReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.WorkbookReader,
new WorkbookReader());
220 workbookReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
221 workbookReader.Execute();
223 ms = GetEntryStream(
"docProps/app.xml", entryLookup);
224 if (ms !=
null && ms.Length > 0)
226 IPluginBaseReader metadataAppReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.MetadataAppReader,
new MetadataAppReader());
227 metadataAppReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
228 metadataAppReader.Execute();
230 ms = GetEntryStream(
"docProps/core.xml", entryLookup);
231 if (ms !=
null && ms.Length > 0)
233 IPluginBaseReader metadataCoreReader = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.MetadataCoreReader,
new MetadataCoreReader());
234 metadataCoreReader.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
235 metadataCoreReader.Execute();
238 IPluginBaseReader relationships = PlugInLoader.GetPlugIn<IPluginBaseReader>(PlugInUUID.RelationshipReader,
new RelationshipReader());
239 ms = GetEntryStream(
"xl/_rels/workbook.xml.rels", entryLookup);
240 relationships.Init(ms, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
241 relationships.Execute();
243 IWorksheetReader worksheetReader = PlugInLoader.GetPlugIn<IWorksheetReader>(PlugInUUID.WorksheetReader,
new WorksheetReader());
244 worksheetReader.SharedStrings = sharedStringsReader.SharedStrings;
245 List<Relationship> relationshipDefinitions = wb.AuxiliaryData.GetDataList<Relationship>(PlugInUUID.RelationshipReader, PlugInUUID.RelationshipEntity);
246 int worksheetVisualIndex = 0;
247 WorksheetDefinition definition;
248 while ((definition = wb.AuxiliaryData.GetData<WorksheetDefinition>(PlugInUUID.WorkbookReader, PlugInUUID.WorksheetDefinitionEntity, worksheetVisualIndex)) !=
null)
250 Relationship relationship = relationshipDefinitions.SingleOrDefault(r => r.RID == definition.RelId);
251 if (relationship ==
null)
253 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");
255 if (!entryLookup.TryGetValue(relationship.Target, out ZipArchiveEntry worksheetEntry))
257 throw new IOException(
"There was an error while reading an XLSX file. The worksheet entry '" + relationship.Target +
"' was not found in the archive");
259 worksheetReader.CurrentWorksheetID = worksheetVisualIndex;
260 if (PlugInLoader.HasQueuePlugins(PlugInUUID.WorksheetInlineReader))
263 MemoryStream wsMs = GetEntryStream(relationship.Target, entryLookup);
264 worksheetReader.Init(wsMs, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
265 worksheetReader.Execute();
270 using (Stream worksheetStream = worksheetEntry.Open())
272 worksheetReader.Init(worksheetStream, wb, readerOptions, ReaderPlugInHandler.HandleInlineQueuePlugins);
273 worksheetReader.Execute();
276 worksheetVisualIndex++;
278 if (wb.Worksheets.Count == 0)
280 throw new IOException(
"No worksheet was found in the workbook");
282 HandleQueuePlugIns(PlugInUUID.ReaderAppendingQueue, entryLookup, ref wb);
283 wb.importInProgress =
false;
284 wb.AuxiliaryData.ClearTemporaryData();
295 private static MemoryStream GetEntryStream(
string name, Dictionary<string, ZipArchiveEntry> entryLookup)
297 if (!entryLookup.TryGetValue(name, out ZipArchiveEntry entry))
301 int capacity = (int)Math.Min(entry.Length,
int.MaxValue);
302 MemoryStream ms =
new MemoryStream(capacity);
303 using (Stream src = entry.Open())
317 private static Dictionary<int, string> GetSequentialStreamNames(
string namePrefix, Dictionary<string, ZipArchiveEntry> entryLookup)
319 Dictionary<int, string> files =
new Dictionary<int, string>();
323 string name = namePrefix + ParserUtils.ToString(index) +
".xml";
324 if (entryLookup.ContainsKey(name))
326 files.Add(index, name);
343 private void HandleQueuePlugIns(
string queueUuid, Dictionary<string, ZipArchiveEntry> entryLookup, ref
Workbook workbook)
345 string lastUuid =
null;
346 IPluginQueueReader queueReader;
350 queueReader = PlugInLoader.GetNextQueuePlugIn<IPluginQueueReader>(queueUuid, lastUuid, out currentUuid);
351 MemoryStream ms =
null;
352 if (queueReader !=
null)
354 if (queueReader is IPluginPackageReader)
356 string streamPartName = (queueReader as IPluginPackageReader).StreamEntryName;
357 if (!
string.IsNullOrEmpty(streamPartName))
359 ms = GetEntryStream(streamPartName, entryLookup);
362 lastUuid = currentUuid;
367 queueReader.Init(ms, workbook, this.readerOptions,
null);
368 queueReader.Execute();
369 lastUuid = currentUuid;
376 }
while (queueReader !=
null);
384 this.inputStream?.Dispose();
385 GC.SuppressFinalize(
this);