[研究][ASP.NET]使用 OdsReaderWriter + DotNetZip 1.15.0 (Ionic.Zip) 讀取匯入 .ods 到資料庫
2021-02-08
ODF release dates:
- ODF 1.0 May 2005
- ODF 1.1 February 2007
- ODF 1.2 September 2012
- ODF 1.3 January 2020
https://wiki.documentfoundation.org/Comparison_of_ODF_software
How to Read and Write ODF/ODS Files (OpenDocument Spreadsheets)
28 Jul 2011
https://www.codeproject.com/Articles/38425/How-to-Read-and-Write-ODF-ODS-Files-OpenDocument-2
所以 odsreadwrite.zip 可能只支援到 ODF, ODS 1.1 版
環境:Visual Studio 2019 + ASP.NET + WebForm + C# + NuGet 安裝 NPOI 2.5.2
<?xml version="1.0" encoding="utf-8"?>
<!--
如需如何設定 ASP.NET 應用程式的詳細資訊,請前往
https://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<connectionStrings>
<add name="TestDBConnectionString" connectionString="Data Source=.;Initial Catalog=TestDB;User ID=sa;Password=P@ssw0rd"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.7.2"/>
<httpRuntime targetFramework="4.7.2"/>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701"/>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
</compilers>
</system.codedom>
<appSettings>
<add key="TempFolder" value="D:\WWW\Temp" />
</appSettings>
</configuration> |
<?xml version="1.0" encoding="utf-8"?> <packages> <package id="DotNetZip" version="1.15.0" targetFramework="net472" /> <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net472" /> </packages> |
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ImportXlsxByOdsReaderWriterDotNetZipTest.Default" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:FileUpload ID="FileUpload1" runat="server" /><br /> <asp:Button ID="Button_Import_Xlsx_by_OdsReaderWriter_DotNetZip_IonicZip" runat="server" Text="匯入(.ods)" OnClick="Button_Import_Xlsx_by_OdsReaderWriter_DotNetZip_IonicZip_Click" /><br /> <br /> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:TestDBConnectionString %>" SelectCommand="SELECT * FROM [Table1]"></asp:SqlDataSource> <asp:GridView ID="GridView1" runat="server" AllowPaging="True" AllowSorting="True" AutoGenerateColumns="False" DataKeyNames="id" DataSourceID="SqlDataSource1"> <Columns> <asp:BoundField DataField="id" HeaderText="id" InsertVisible="False" ReadOnly="True" SortExpression="id" /> <asp:BoundField DataField="FieldText" HeaderText="FieldText" SortExpression="FieldText" /> <asp:BoundField DataField="FieldDateTime" HeaderText="FieldDateTime" SortExpression="FieldDateTime" /> <asp:BoundField DataField="FieldInt" HeaderText="FieldInt" SortExpression="FieldInt" /> <asp:CheckBoxField DataField="FieldBit" HeaderText="FieldBit" SortExpression="FieldBit" /> </Columns> </asp:GridView> <asp:Label ID="Label_ExportImportMSG" runat="server"></asp:Label><br /> <asp:Label ID="Label_MSG1" runat="server"></asp:Label> </div> </form> </body> </html> |
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.IO; using System.Linq; using System.Web; using System.Web.Configuration; using System.Web.UI; using System.Web.UI.WebControls; using OdsReadWrite; // 要 using namespace ,才能存取到 OdsReadWriter 這個 class namespace ImportXlsxByOdsReaderWriterDotNetZipTest { public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button_Import_Xlsx_by_OdsReaderWriter_DotNetZip_IonicZip_Click(object sender, EventArgs e) { /* OdsReaderWriter.cs 和 template.ods 取自下面網站 How to Read and Write ODF/ODS Files (OpenDocument Spreadsheets) https://www.codeproject.com/Articles/38425/How-to-Read-and-Write-ODF-ODS-Files-OpenDocument-2 template.ods 請自己用 Excel、OpenOffice 或 LibreOffice 的 Calc 建立一個空的即可。 NuGet 要安裝 Ionic.Zip 套件。(Ionic.Zip 已經淘汰,請改用DotNetZip) */ Label_MSG1.Text = ""; Label_ExportImportMSG.Text = ""; string queryString = ""; string errMsg = ""; string url = ""; string msg = ""; string fieldText = ""; string fieldDateTime = ""; DateTime fieldDateTime2 = DateTime.Now; string fieldInt = ""; string fieldBit = ""; int rowCount = 0; int columnCount = 0; int successCount = 0; int lineNo = 0; try { if (!FileUpload1.HasFile) { Label_ExportImportMSG.Text = "<font color=red>請選擇.ods檔案</font>"; Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript' defer>alert('請選擇.ods檔案。');</script>"); return; } else { string fd = (string)ConfigurationManager.AppSettings["TempFolder"] + @"\"; if (!System.IO.Directory.Exists(fd)) { System.IO.Directory.CreateDirectory(fd); } string ext = System.IO.Path.GetExtension(FileUpload1.FileName).ToLower(); //if (!((ext == ".ods") || (ext == ".csv"))) if (!(ext == ".ods")) { Label_ExportImportMSG.Text = "<font color=red>請上傳副檔名.ods檔案</font>"; Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript'>alert('" + "請上傳副檔名.ods檔案!" + "');</script>"); return; } string fileName = fd + FileUpload1.FileName; FileUpload1.SaveAs(fileName); string mainFileName = Path.GetFileNameWithoutExtension(FileUpload1.FileName); //string openFilename = "D:\\WWW\\TempImageFiles\\" + mainFileName + "-匯入結果.ods"; string openFilename = fd + mainFileName + "-匯入結果" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff") + ".ods"; FileUpload1.SaveAs(openFilename); //queryString = @"DELETE FROM [dbo].[Table1] WHERE id=@id; "; queryString = @"DELETE FROM [dbo].[Table1]; "; using (SqlConnection connection = new SqlConnection( WebConfigurationManager.ConnectionStrings["TestDBConnectionString"].ConnectionString)) { SqlCommand command = new SqlCommand(queryString, connection); connection.Open(); command.ExecuteNonQuery(); } OdsReaderWriter odsFile = new OdsReaderWriter(); DataSet odsDataSet = odsFile.ReadOdsFile(openFilename); DataTable table = odsDataSet.Tables[0]; // 只抓第一個工作表,第二個工作表是說明 rowCount = table.Rows.Count; columnCount = table.Columns.Count; for (int i = 0; i <= table.Rows.Count - 1; i++) { fieldText = ""; fieldDateTime = ""; fieldInt = ""; fieldBit = ""; lineNo = i; if (i == 0) { // 標題列不讀取 } else { // 非標題列 fieldText = table.Rows[i][0].ToString().Trim(); if (fieldText == "") { // 第一欄位必填,null 或 "" 就假設後面都沒有資料 errMsg = "#1匯入.ods共" + rowCount + "橫列" + columnCount + "行(欄位) (含標題列,若看不到那麼多橫列,可能填寫了不可見字元),第" + (i - 1) + "筆 (Excel 第" + i.ToString() + "列) 第1欄位必填沒有資料,匯入中斷,若資料已經全部匯入,可不用理會訊息。\\n"; //ClientScript.RegisterStartupScript(GetType(), "message", "<script>alert('" + errMsg + "');</script>"); Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript'>alert('" + errMsg + "');</script>"); GridView1.DataBind(); // 更新畫面 return; } else { // 該列假設有資料 fieldDateTime = table.Rows[i][1].ToString().Trim(); bool convert = DateTime.TryParse(fieldDateTime, out fieldDateTime2); if (convert) { fieldDateTime = fieldDateTime2.ToString("yyyy/MM/dd HH:mm:ss.fff"); } else { fieldDateTime = null; } fieldInt = table.Rows[i][2].ToString().Trim(); fieldBit = table.Rows[i][3].ToString().Trim(); // -------------------------------------------------------------------------------- // e.Values 中欄位名稱的大小寫要符合前端 Bind 的欄位名稱 // 檢察欄位長度、必填、關聯要求 // errMsg = CheckRecord(fieldText,fieldDateTime,fieldInt,fieldBit); // if (errMsg != "") // { // Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", // "<script language='javascript' defer>alert('第" + lineNo + // "筆匯入失敗,匯入中斷!" + errMsg + "');</script>"); // return; // } // -------------------------------------------------------------------------------- queryString = @" INSERT INTO [dbo].[Table1] ([FieldText] ,[FieldDateTime] ,[FieldInt] ,[FieldBit]) VALUES (N'" + fieldText + "'" + ",'" + fieldDateTime + "'" + ",'" + fieldInt + "'" + ",'" + fieldBit + "'); "; using (SqlConnection connection = new SqlConnection( WebConfigurationManager.ConnectionStrings["TestDBConnectionString"].ConnectionString)) { SqlCommand command = new SqlCommand(queryString, connection); //command.Parameters.Clear(); //command.Parameters.AddWithValue("@Value", value); connection.Open(); command.ExecuteNonQuery(); successCount++; } } // if (fieldText == "") } // if (i == 0) } // for //string url = "/List.aspx?Ref1=" + Request.QueryString["Ref1"].ToString() + "&Ref2=" + // Request.QueryString["Ref2"].ToString() + "&Ref3=" + Request.QueryString["Ref3"].ToString(); url = "#"; msg = "#2匯入.ods共" + rowCount + "橫列" + columnCount + "行(欄位) (含標題列,若看不到那麼多橫列,可能填寫了不可見字元),成功匯入" + successCount + "筆。\\n"; ClientScript.RegisterStartupScript(GetType(), "message", "<script>alert('" + msg + "');location.href='" + url + "';</script>"); } // if (!FileUpload1.HasFile) } catch (Exception ex) { string exceptionMsg = "不明錯誤。"; if (ex != null) { exceptionMsg = ex.Message.ToString(); } Label_MSG1.ForeColor = System.Drawing.Color.Red; Label_MSG1.Text = errMsg + "<br />" + exceptionMsg; errMsg = "#3匯入.ods共" + rowCount + "橫列" + columnCount + "行(欄位) (含標題列,若看不到那麼多橫列,可能填寫了不可見字元),第" + (lineNo - 1) + "筆 (Excel 第" + lineNo.ToString() + "列)匯入失敗。\\n"; //ClientScript.RegisterStartupScript(GetType(), "message", "<script>alert('" + errMsg + "');</script>"); Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript'>alert('" + errMsg + "');</script>"); // 會因為 Exception 內容關係,JavaScript alert 對話盒視窗可能不會跳出顯示 Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript'>alert('" + exceptionMsg + "');</script>"); } GridView1.DataBind(); // 更新畫面 } } } |
OdsReaderWriter.cs
https://www.codeproject.com/Articles/38425/How-to-Read-and-Write-ODF-ODS-Files-OpenDocument-2
using System; using System.Data; using System.Globalization; using System.IO; using System.Reflection; using System.Xml; // Ionic.Zip 已經淘汰,請改用 DotNetZip // 不管 NuGet 安裝 DotNetZip 或 Ionic.Zip,Using 都是 Ionic.Zip using Ionic.Zip; namespace OdsReadWrite { internal sealed class OdsReaderWriter { // Namespaces. We need this to initialize XmlNamespaceManager so that we can search XmlDocument. private static string[,] namespaces = new string[,] { {"table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0"}, {"office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0"}, {"style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0"}, {"text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0"}, {"draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"}, {"fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"}, {"dc", "http://purl.org/dc/elements/1.1/"}, {"meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"}, {"number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"}, {"presentation", "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"}, {"svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"}, {"chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0"}, {"dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"}, {"math", "http://www.w3.org/1998/Math/MathML"}, {"form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0"}, {"script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0"}, {"ooo", "http://openoffice.org/2004/office"}, {"ooow", "http://openoffice.org/2004/writer"}, {"oooc", "http://openoffice.org/2004/calc"}, {"dom", "http://www.w3.org/2001/xml-events"}, {"xforms", "http://www.w3.org/2002/xforms"}, {"xsd", "http://www.w3.org/2001/XMLSchema"}, {"xsi", "http://www.w3.org/2001/XMLSchema-instance"}, {"rpt", "http://openoffice.org/2005/report"}, {"of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2"}, {"rdfa", "http://docs.oasis-open.org/opendocument/meta/rdfa#"}, {"config", "urn:oasis:names:tc:opendocument:xmlns:config:1.0"} }; // Read zip stream (.ods file is zip file). private ZipFile GetZipFile(Stream stream) { return ZipFile.Read(stream); } // Read zip file (.ods file is zip file). private ZipFile GetZipFile(string inputFilePath) { return ZipFile.Read(inputFilePath); } private XmlDocument GetContentXmlFile(ZipFile zipFile) { // Get file(in zip archive) that contains data ("content.xml"). ZipEntry contentZipEntry = zipFile["content.xml"]; // Extract that file to MemoryStream. Stream contentStream = new MemoryStream(); contentZipEntry.Extract(contentStream); contentStream.Seek(0, SeekOrigin.Begin); // Create XmlDocument from MemoryStream (MemoryStream contains content.xml). XmlDocument contentXml = new XmlDocument(); contentXml.Load(contentStream); return contentXml; } private XmlNamespaceManager InitializeXmlNamespaceManager(XmlDocument xmlDocument) { XmlNamespaceManager nmsManager = new XmlNamespaceManager(xmlDocument.NameTable); for (int i = 0; i < namespaces.GetLength(0); i++) nmsManager.AddNamespace(namespaces[i, 0], namespaces[i, 1]); return nmsManager; } /// <summary> /// Read .ods file and store it in DataSet. /// </summary> /// <param name="inputFilePath">Path to the .ods file.</param> /// <returns>DataSet that represents .ods file.</returns> public DataSet ReadOdsFile(string inputFilePath) { ZipFile odsZipFile = this.GetZipFile(inputFilePath); // Get content.xml file XmlDocument contentXml = this.GetContentXmlFile(odsZipFile); // Initialize XmlNamespaceManager XmlNamespaceManager nmsManager = this.InitializeXmlNamespaceManager(contentXml); DataSet odsFile = new DataSet(Path.GetFileName(inputFilePath)); foreach (XmlNode tableNode in this.GetTableNodes(contentXml, nmsManager)) odsFile.Tables.Add(this.GetSheet(tableNode, nmsManager)); return odsFile; } // In ODF sheet is stored in table:table node private XmlNodeList GetTableNodes(XmlDocument contentXmlDocument, XmlNamespaceManager nmsManager) { return contentXmlDocument.SelectNodes("/office:document-content/office:body/office:spreadsheet/table:table", nmsManager); } private DataTable GetSheet(XmlNode tableNode, XmlNamespaceManager nmsManager) { DataTable sheet = new DataTable(tableNode.Attributes["table:name"].Value); XmlNodeList rowNodes = tableNode.SelectNodes("table:table-row", nmsManager); int rowIndex = 0; foreach (XmlNode rowNode in rowNodes) this.GetRow(rowNode, sheet, nmsManager, ref rowIndex); return sheet; } private void GetRow(XmlNode rowNode, DataTable sheet, XmlNamespaceManager nmsManager, ref int rowIndex) { XmlAttribute rowsRepeated = rowNode.Attributes["table:number-rows-repeated"]; if (rowsRepeated == null || Convert.ToInt32(rowsRepeated.Value, CultureInfo.InvariantCulture) == 1) { while (sheet.Rows.Count < rowIndex) sheet.Rows.Add(sheet.NewRow()); DataRow row = sheet.NewRow(); XmlNodeList cellNodes = rowNode.SelectNodes("table:table-cell", nmsManager); int cellIndex = 0; foreach (XmlNode cellNode in cellNodes) this.GetCell(cellNode, row, nmsManager, ref cellIndex); sheet.Rows.Add(row); rowIndex++; } else { rowIndex += Convert.ToInt32(rowsRepeated.Value, CultureInfo.InvariantCulture); } // sheet must have at least one cell if (sheet.Rows.Count == 0) { sheet.Rows.Add(sheet.NewRow()); sheet.Columns.Add(); } } private void GetCell(XmlNode cellNode, DataRow row, XmlNamespaceManager nmsManager, ref int cellIndex) { XmlAttribute cellRepeated = cellNode.Attributes["table:number-columns-repeated"]; if (cellRepeated == null) { DataTable sheet = row.Table; while (sheet.Columns.Count <= cellIndex) sheet.Columns.Add(); row[cellIndex] = this.ReadCellValue(cellNode); cellIndex++; } else { cellIndex += Convert.ToInt32(cellRepeated.Value, CultureInfo.InvariantCulture); } } private string ReadCellValue(XmlNode cell) { XmlAttribute cellVal = cell.Attributes["office:value"]; if (cellVal == null) return String.IsNullOrEmpty(cell.InnerText) ? null : cell.InnerText; else return cellVal.Value; } /// <summary> /// Writes DataSet as .ods file. /// </summary> /// <param name="odsFile">DataSet that represent .ods file.</param> /// <param name="outputFilePath">The name of the file to save to.</param> public void WriteOdsFile(DataSet odsFile, string outputFilePath) { ZipFile templateFile = this.GetZipFile(Assembly.GetExecutingAssembly().GetManifestResourceStream("OdsReadWrite.template.ods")); //ZipFile templateFile = this.GetZipFile(File.OpenRead(@"C:\CODE\ODSExport\ODSExport\bin\template.ods")); //ZipFile templateFile = this.GetZipFile(File.OpenRead(Server.MapPath("/") + "template.ods"); //ZipFile templateFile = this.GetZipFile(File.OpenRead(HttpContext.Current.Server.MapPath("/template.ods"))); XmlDocument contentXml = this.GetContentXmlFile(templateFile); XmlNamespaceManager nmsManager = this.InitializeXmlNamespaceManager(contentXml); XmlNode sheetsRootNode = this.GetSheetsRootNodeAndRemoveChildrens(contentXml, nmsManager); foreach (DataTable sheet in odsFile.Tables) this.SaveSheet(sheet, sheetsRootNode); this.SaveContentXml(templateFile, contentXml); templateFile.Save(outputFilePath); } private XmlNode GetSheetsRootNodeAndRemoveChildrens(XmlDocument contentXml, XmlNamespaceManager nmsManager) { XmlNodeList tableNodes = this.GetTableNodes(contentXml, nmsManager); XmlNode sheetsRootNode = tableNodes.Item(0).ParentNode; // remove sheets from template file foreach (XmlNode tableNode in tableNodes) sheetsRootNode.RemoveChild(tableNode); return sheetsRootNode; } private void SaveSheet(DataTable sheet, XmlNode sheetsRootNode) { XmlDocument ownerDocument = sheetsRootNode.OwnerDocument; XmlNode sheetNode = ownerDocument.CreateElement("table:table", this.GetNamespaceUri("table")); XmlAttribute sheetName = ownerDocument.CreateAttribute("table:name", this.GetNamespaceUri("table")); sheetName.Value = sheet.TableName; sheetNode.Attributes.Append(sheetName); this.SaveColumnDefinition(sheet, sheetNode, ownerDocument); this.SaveRows(sheet, sheetNode, ownerDocument); sheetsRootNode.AppendChild(sheetNode); } private void SaveColumnDefinition(DataTable sheet, XmlNode sheetNode, XmlDocument ownerDocument) { XmlNode columnDefinition = ownerDocument.CreateElement("table:table-column", this.GetNamespaceUri("table")); XmlAttribute columnsCount = ownerDocument.CreateAttribute("table:number-columns-repeated", this.GetNamespaceUri("table")); columnsCount.Value = sheet.Columns.Count.ToString(CultureInfo.InvariantCulture); columnDefinition.Attributes.Append(columnsCount); sheetNode.AppendChild(columnDefinition); } private void SaveRows(DataTable sheet, XmlNode sheetNode, XmlDocument ownerDocument) { DataRowCollection rows = sheet.Rows; for (int i = 0; i < rows.Count; i++) { XmlNode rowNode = ownerDocument.CreateElement("table:table-row", this.GetNamespaceUri("table")); this.SaveCell(rows[i], rowNode, ownerDocument); sheetNode.AppendChild(rowNode); } } private void SaveCell(DataRow row, XmlNode rowNode, XmlDocument ownerDocument) { object[] cells = row.ItemArray; for (int i = 0; i < cells.Length; i++) { XmlElement cellNode = ownerDocument.CreateElement("table:table-cell", this.GetNamespaceUri("table")); if (row[i] != DBNull.Value) { // We save values as text (string) XmlAttribute valueType = ownerDocument.CreateAttribute("office:value-type", this.GetNamespaceUri("office")); valueType.Value = "string"; cellNode.Attributes.Append(valueType); XmlElement cellValue = ownerDocument.CreateElement("text:p", this.GetNamespaceUri("text")); cellValue.InnerText = row[i].ToString(); cellNode.AppendChild(cellValue); } rowNode.AppendChild(cellNode); } } private void SaveContentXml(ZipFile templateFile, XmlDocument contentXml) { templateFile.RemoveEntry("content.xml"); MemoryStream memStream = new MemoryStream(); contentXml.Save(memStream); memStream.Seek(0, SeekOrigin.Begin); templateFile.AddEntry("content.xml", memStream); } private string GetNamespaceUri(string prefix) { for (int i = 0; i < namespaces.GetLength(0); i++) { if (namespaces[i, 0] == prefix) return namespaces[i, 1]; } throw new InvalidOperationException("Can't find that namespace URI"); } } } |
實際使用經驗,.ods元件和.ods檔案相容性,比不上.xlsx元件和.xlsx檔案的相容性。
.ods元件有時候會讀取不到.ods檔案欄位中資料,甚至很多橫列內容相同時,可能只能讀取到一筆。(敝人自己做不出,但某些使用者不知如何做出的,拿來匯入確實有問題)
不管是 OdsReaderWriter + DotNetZip 或商業付費的 Aspose.Cells 都如此,但是 Aspose.Cells 正常機率高於 OdsReaderWriter + DotNetZip 方式。
.ods元件讀.ods檔案正常機率有多高,粗估高達 99% 以上,但約萬名使用者,1% 異常就很有得應付處理了。
(完)
沒有留言:
張貼留言