开发者

C# Winform使用NPOI获取Word内容的实战指南

目录
  • 为什么选择NPOI?
  • 模块1:环境准备与项目搭建
    • 1.1 安装NPOI库:告别“Hello World”陷阱
    • 1.2 创建Winform窗体:从空白到功能按钮
  • 模块2:核心功能实现——段落与表格解析
    • 2.1 读取.docx文件:XwpFDocument的魔法
    • 2.2 提取表格数据:嵌套循环的“陷阱”与“解药”
  • 模块3:高级功能——图片与样式提取
    • 3.1 提取图片:Base64编码的“隐藏通道”
    • 3.2 保留样式:字体、颜色的“克隆术”
  • 模块4:实战案例——从Word到数据库的完整链路
    • 4.1 数据映射实体类:字段命名的艺术
    • 4.2 数据库写入:异步操作的“性能炸弹”
  • 模块5:异常处理与性能调优
    • 5.1 常见错误与解决方案
    • 5.2 内存优化:大型文档的“瘦身术”
  • 从入门到精通的“跃迁之路”

    为什么选择NPOI?

    NPOI核心优势

    1. 无需Office依赖:纯.NET库解析.doc/.docx
    2. 支持复杂结构:表格、段落、图片、样式全解析
    3. 高性能低内存:比COM快5-10倍,内存占用降低70%

    模块1:环境准备与项目搭建

    1.1 安装NPOI库:告别“Hello World”陷阱

    “NuGet安装后找不到类?90%的初学者都踩过这个坑!”

    // Visual Studio NuGet命令行  
    Install-Package NPOI -Version 2.5.4  
    Install-Package NPOI.XWPF.UserModel -Version 2.5.4  
    Install-Package NPOI.HWPF -Version 2.5.4  
    

    关键点

    • HWPF:处理.doc(旧版Word)
    • XWPF:处理.docx(新版Word)
    • 版本匹配:确保项目.NET Framework ≥ 4.6

    1.2 创建Winform窗体:从空白到功能按钮

    “按钮点击事件写错命名空间?看这里!”

    // Form1.cs  
    public partial class Form1 : Form  
    {  
        public Form1()  
        {  
            InitializeComponent();  
            // 初始化按钮  
            Button btnParse = new Button();  
            btnParse.Text = "解析Word";  
            btnParse.Location = new Point(50, 50);  
            btnParse.Click += BtnParse_Click;  
            this.Controls.Add(btnParse);  
        }  
    
        private void BtnParse_Click(object sender, EventArgs e)  
        {  
            try  
            {  
                OpenFileDialog openFileDialog = new OpenFileDialog();  
                openFileDialog.Filter = "Word文档|*.doc;*.docx";  
                if (openFileDialog.ShowDialog() == DialogResult.OK)  
                {  
                    string filePath = openFileDialog.FileName;  
                    ParseWordDocument(filePath);  
                }  
            }  
            catch (Exception ex)  
            {  
                MessageBox.Show($"解析失败:{ex.Message}");  
            }  
        }  
    }
    

    调试技巧

    • 异常捕获:务必包裹try-catch,防止程序崩溃
    • 文件过滤:通过Filter属性限定文件类型

    模块2:核心功能实现——段落与表格解析

    2.1 读取.docx文件:XWPFDocument的魔法

    “段落遍历卡在空行?你需要知道RowNum的隐藏规则!”

    private void ParseWordDocument(string filePath)  
    {  
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))  
        {  
            IWorkbook workbook = null;  
            if (filePath.EndsWith(".docx"))  
            {  
                workbook = new XSSFWorkbook(fs);  
            }  
            else if (filePath.EndsWith(android".doc"))  
            {  
                workbook = new HSSFWorkbook(fs);  
            }  
    
            if (workbook != null)  
            {  
                foreach (ISheet sheet in workbook)  
                {  
                    foreach (IRow row in sheet)  
                    {  
                        foreach (ICell cell in row)  
                        {  
                            Console.WriteLine($"单元格内容:{cell.ToString()}");  
                        }  
                    }  
                }  
            }  
        }  
    }
    

    进阶优化

    • 类型判断:通过cell.CellType处理数字/日期
    • 样式保留cell.GetCellStyle()获取字体、颜色

    2.2 提取表格数据:嵌套循环的“陷阱”与“解药”

    “表格行数少1?RowNum从0开始计数的真相!”

    private void ExtractTableData(XWPFDocument doc)  
    {  
        foreach (XWPFTable table in doc.Tables)  
        {  
            Console.WriteLine("发现表格:");  
            for (int i = 0; i < table.Rows.Count; i++)  
            {  
                XWPFTableRow row = table.Rows[i];  
                for (int j = 0; j < row.Cells.Count; j++)  
                {  
                    XWPFTableCellhttp://www.devze.com cell = row.Cells[j];  
                    // 处理单元格中的段落  
                    string cellText = string.Join("\n", cell.Paragjsraphs.Select(p => p.Text));  
             编程       Console.WriteLine($"行{i}列{j}:{cellText}");  
                }  
            }  
        }  
    }
    

    关键技巧

    • 段落合并string.Join处理多段落单元格
    • 表格定位:通过table.GetCTTbl().GetPos()获取位置信息

    模块3:高级功能——图片与样式提取

    3.1 提取图片:Base64编码的“隐藏通道”

    “图片保存路径错误?你需要绝对路径+文件名哈希!”

    private void ExtractImages(XWPFDocument doc, string outputDir)  
    {  
        if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir);  
    
        int imageIndex = 0;  
        foreach (XWPFPictureData picture in doc.GetAllPictures())  
        {  
            string ext = GetImageExtension(picture.PictureType);  
            string imagePath = Path.Combine(outputDir, $"image_{imageIndex++}{ext}");  
    
            using (FileStream fs = new FileStream(imagePath, FileMode.Create))  
            {  
                picture.WriteImageContent(fs);  
                Console.WriteLine($"图片已保存:{imagePath}");  
            }  
        }  
    }  
    
    private string GetImageExtension(int pictureType)  
    {  
        switch (pictureType)  
        {  
            case 2: return ".jpg";  
            case 3: return ".png";  
            case 4: return ".gif";  
            default: return ".bin";  
        }  
    }
    

    注意事项

    • 格式识别PictureType可能返回未知类型
    • 内存释放using语句确保流正确关闭

    3.2 保留样式:字体、颜色的“克隆术”

    “样式丢失?你需要深入ICellStyle的每个属性!”

    private void PreserveStyles(ICell cell)  
    {  
        ICellStyle style = cell.GetCellStyle();  
        Console.WriteLine($"字体:{style.Font.FontName}");  
        Console.WriteLine($"字号:{style.Font.FontSize}");  
        Console.WriteLine($"颜色:{style.FillForegroundColor}");  
        Console.WriteLine($"加粗:{style.Font.IsBold}");  
    }
    

    扩展应用

    • 样式映射:将Excel样式转换为html/css
    • 条件格式:根据背景色标记重要数据

    模块4:实战案例——从Word到数据库的完整链路

    4.1 数据映射实体类:字段命名的艺术

    “数据库字段命名混乱?用[Column]特性统一规范!”

    [Table("WordData")]  
    public class WordDataEntity  
    {  
        [Key]  
        public int Id { get; set; }  
    
        [Column("OriginalText")]  
        public string Content { get; set; }  
    
        [Column("ExtractTime")]  
        public DateTime ExtractedAt { get; set; }  
    }
    

    EF Core配置

    • 自动迁移Add-Migration InitialCreate
    • 批量插入SaveChanges() vs BulkInsert

    4.2 数据库写入:异步操作的“性能炸弹”

    “同步阻塞导致UI卡顿?异步编程是关键!”

    private async Task SaveToDatabaseAsync(List<WordDataEntity> data)  
    {  
        using (var context = new WordDataContext())  
        {  
            context.WordData.AddRange(data);  
            await context.SaveChangesAsyjavascriptnc();  
        }  
    }
    

    性能优化

    • 批量提交:每100条数据提交一次
    • 连接池配置Pooling=true;Max Pool Size=200;

    模块5:异常处理与性能调优

    5.1 常见错误与解决方案

    “文件损坏导致解析失败?你需要健壮的异常处理!”

    private void SafeParse(string filePath)  
    {  
        try  
        {  
            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))  
            {  
                if (filePath.EndsWith(".docx"))  
                {  
                    var doc = new XWPFDocument(fs);  
                    // 解析逻辑  
                }  
                else if (filePath.EndsWith(".doc"))  
                {  
                    var doc = new HWPFDocument(fs);  
                    // 解析逻辑  
                }  
            }  
        }  
        catch (IOException ex)  
        {  
            Console.WriteLine($"IO异常:{ex.Message}");  
        }  
        catch (InvalidDataException ex)  
        {  
            Console.WriteLine($"文件格式错误:{ex.Message}");  
        }  
    }
    

    日志记录

    • SerilogLog.Information("解析开始")
    • 文件分级:按日期生成日志文件

    5.2 内存优化:大型文档的“瘦身术”

    “500MB文档导致内存爆表?流式处理是关键!”

    private void StreamParse(string filePath)  
    {  
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))  
        {  
            if (filePath.EndsWith(".docx"))  
            {  
                using (var doc = new XWPFDocument(fs))  
                {  
                    foreach (var para in doc.Paragraphs)  
                    {  
                        ProcessParagraph(para);  
                    }  
                }  
            }  
        }  
    }
    

    关键策略

    • 逐行处理:避免一次性加载全部内容
    • 对象池:复用StringBuilder等对象

    从入门到精通的“跃迁之路”

    “掌握NPOI后,你的生产力直接提升10倍!”

    核心思想

    • 分层架构:UI层、业务层、数据层分离
    • 异常防御:全面覆盖所有可能错误场景
    • 性能优先:异步处理+流式解析

    进阶方向

    • OCR集成:处理扫描版PDF转Word
    • RPA自动化:结合UIPath实现端到端流程
    • 微服务化:将解析功能封装为REST API

    终极目标

    • 构建企业级文档处理平台:支持Word/Excel/PDF多格式互转

    以上就是C# Winform使用NPOI获取Word内容的实战指南的详细内容,更多关于C# NPOI获取Word内容的资料请关注编程客栈(www.devze.com)其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜