2025年6月2日 星期一

[研究]ASP.NET, OdsExporter 使用 C# 寫入匯出 .ods (一)

[研究]ASP.NET, OdsExporter 使用 C# 寫入匯出 .ods (一)

2025-06-02

環境:Visual Studio 2022 + ASP.NET + WebForm + Web Application + C# + SQL Server 2019 + SQL Server Management Studio (SSMS) 20.2

********************************************************************************

參考

Reading OpenDocument spreadsheets using C#
August 24, 2012
https://gunnarpeipman.com/reading-opendocument-spreadsheets-using-c/

********************************************************************************

********************************************************************************

OdsExporter.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;

namespace WebApplication1
{
    public class OdsExporter
    {
        public MemoryStream ExportToStream(DataTable table)
        {
            var memoryStream = new MemoryStream();

            using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true))
            {
                // mimetype(必須為第一個,且不壓縮)
                var mimetypeEntry = archive.CreateEntry("mimetype", CompressionLevel.NoCompression);
                using (var writer = new StreamWriter(mimetypeEntry.Open(), Encoding.ASCII))
                {
                    writer.Write("application/vnd.oasis.opendocument.spreadsheet");
                }

                // META-INF/manifest.xml
                var manifestEntry = archive.CreateEntry("META-INF/manifest.xml");
                using (var writer = new StreamWriter(manifestEntry.Open(), Encoding.UTF8))
                {
                    writer.Write(@"<?xml version=""1.0"" encoding=""UTF-8""?>
<manifest:manifest xmlns:manifest=""urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"">
  <manifest:file-entry manifest:full-path=""/"" manifest:media-type=""application/vnd.oasis.opendocument.spreadsheet""/>
  <manifest:file-entry manifest:full-path=""content.xml"" manifest:media-type=""text/xml""/>
</manifest:manifest>");
                }

                // content.xml
                var contentEntry = archive.CreateEntry("content.xml");
                using (var writer = new StreamWriter(contentEntry.Open(), Encoding.UTF8))
                {
                    writer.Write(GenerateContentXml(table));
                }
            }

            memoryStream.Position = 0; // 重設串流位置供下載
            return memoryStream;
        }

        private string GenerateContentXml(DataTable table)
        {
            XNamespace office = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
            XNamespace tableNs = "urn:oasis:names:tc:opendocument:xmlns:table:1.0";
            XNamespace text = "urn:oasis:names:tc:opendocument:xmlns:text:1.0";

            var document = new XDocument(
                new XDeclaration("1.0", "UTF-8", "yes"),
                new XElement(office + "document-content",
                    new XAttribute(XNamespace.Xmlns + "office", office),
                    new XAttribute(XNamespace.Xmlns + "table", tableNs),
                    new XAttribute(XNamespace.Xmlns + "text", text),
                    new XAttribute(office + "version", "1.2"),
                    new XElement(office + "body",
                        new XElement(office + "spreadsheet",
                            new XElement(tableNs + "table",
                                new XAttribute(tableNs + "name", "Sheet1"),

                                // 欄位列
                                new XElement(tableNs + "table-row",
                                    from DataColumn col in table.Columns
                                    select new XElement(tableNs + "table-cell",
                                        new XAttribute(office + "value-type", "string"),
                                        new XElement(text + "p", col.ColumnName)
                                    )
                                ),

                                // 資料列
                                from DataRow row in table.Rows
                                select new XElement(tableNs + "table-row",
                                    from DataColumn col in table.Columns
                                    select new XElement(tableNs + "table-cell",
                                        new XAttribute(office + "value-type", "string"),
                                        new XElement(text + "p", row[col]?.ToString() ?? "")
                                    )
                                )
                            )
                        )
                    )
                )
            );

            return document.ToString(SaveOptions.DisableFormatting);
        }
    }
}

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.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">
        <asp:Button ID="btnExport" runat="server" Text="從資料庫匯出為 ODS" OnClick="btnExport_Click" /><br />
        <asp:Label ID="lblMessage" runat="server"></asp:Label>
    </form>
</body>
</html>


Default.aspx.cs

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

namespace WebApplication1
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
        protected void btnExport_Click(object sender, EventArgs e)
        {
            try
            {
                // 1. 從資料庫取得 DataTable
                var table = GetDataTableFromDatabase();

                if (table == null || table.Rows.Count == 0)
                {
                    lblMessage.Text = "資料表為空,無法匯出。";
                    return;
                }

                // 2. 匯出成 ODS 並下載
                OdsExporter exporter = new OdsExporter();
                using (var stream = exporter.ExportToStream(table))
                {
                    Response.Clear();
                    Response.ContentType = "application/vnd.oasis.opendocument.spreadsheet";
                    Response.AddHeader("Content-Disposition", "attachment; filename=匯出.ods");
                    Response.BinaryWrite(stream.ToArray());
                    Response.End();
                }
            }
            catch (Exception ex)
            {
                lblMessage.Text = "匯出時發生錯誤:" + ex.Message;
            }
        }

        private DataTable GetDataTableFromDatabase()
        {
            string connectionString = ConfigurationManager.ConnectionStrings["MyDBConnectionString"].ConnectionString;

            using (var conn = new SqlConnection(connectionString))
            using (var cmd = new SqlCommand("SELECT [SN], [Field1], [Field2] FROM [MyDB].[dbo].[MyTable]", conn))
            using (var adapter = new SqlDataAdapter(cmd))
            {
                var table = new DataTable();
                conn.Open();
                adapter.Fill(table);
                return table;
            }
        }
    }
}

結果


(完)

相關

[研究]ASP.NET, OdsExporter 使用 C# 寫入匯出 .ods (一)

[研究]ASP.NET, OdsImporter 使用 C# 讀取匯入 .ods (三)用 System.IO.Compression 取代 SharpZipLib

[研究]ASP.NET, OdsImporter 使用 C# 讀取匯入 .ods (二)欄位數量不固定

[研究]ASP.NET, OdsImporter 使用 C# 讀取匯入 .ods (一)


沒有留言:

張貼留言