news 2026/10/18 19:56:09

Java导出Word实战:基于Apache POI XWPF生成docx的完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java导出Word实战:基于Apache POI XWPF生成docx的完整方案

简介:面向需要实现Word文档导出的Java开发者,资源包提供一套完整可运行的源码方案。方案借助Apache POI类库,覆盖了创建文档对象、插入本地图片、设置中文字体、添加表格以及调整段落格式等关键环节,可直接支撑报表生成、数据批量导出等自动化办公场景。压缩包内共十四个文件,核心为Java源文件,同时包含构建配置、导入说明、示例数据表格、测试图片和最终生成的效果文档,整包大小约二十五KB,结构紧凑,便于快速下载与研读。目前已有四十五人学习使用。资源中的代码并非零散片段,而是完整的工程样例,细致展示了首页设计、正文排版、表格边框底纹调整、图片的二进制流读取与插入等具体实现;配套的导入指南区分了两种主流集成开发环境,帮助不同习惯的开发者顺利搭建运行环境。通过研读,读者既能掌握POI操作文档的核心用法,也能学习依赖配置与代码组织上的良好实践,对提升实际项目中的文档生成能力颇有帮助。

1. Java导出Word:这个需求比想象中麻烦得多

很多做后端的人第一次接到“导出Word”需求时,都会觉得无非是拼个字符串、写个文件的事,可真动手就会发现坑一个接一个:中文乱码、docx打不开、表格宽度怎么设都不对、图片一插进去版式就稀碎。我做过几个带有报表导出功能的模拟项目后,把这套流程沉淀成了可运行的源码片段,基于Apache POI的XWPF实现纯Java导出.docx,不需要本机安装Office,Linux服务端也能用。本文写给那些需要把数据从数据库或内存对象落成Word文档的开发者——无论是订单报表、合同文档还是档案导出,这套方案都能直接上手。从选型到踩坑,再到验证手段,讲的都是我自己在业务里反复试出来的经验,照着跑就能避开大多数翻车点。

2. 导出Word的选型:为什么最终都绕不开POI的XWPF

2.1 那些看起来省事的方案,实际用起来各有各的坑

Java生成Word文档的常见路线其实有好几条,但每一条在真实业务里都有明显短板。先说差点走通的freemarker加XML模板方案:Word的docx文件本质是一个zip压缩包,里面是若干XML文件,理论上可以把docx后缀改成zip解压出来,把document.xml当作模板,用freemarker填充占位符再重新打包。这个思路我在某跨平台系统里实际验证过,遇到两个问题——模板里的特殊字符比如&、<、>必须先做XML转义,否则填充后整个文档结构直接损坏;更麻烦的是,一旦模板里带着页眉页脚、复杂表格或者修订记录,重新打包时很容易把word内部的关联关系弄丢,生成的文件Word一打开就提示修复。这条路适合格式极其固定的内部单据,但开发调试成本高,不适合做通用导出能力。

再说poi-tl这套开源封装,它把模板渲染的体验做得相当好,{{title}}这种占位符风格很接近程序员直觉,业务方也容易接受。我见过不少新项目用poi-tl做合同导出,开发效率确实高。但它本质是包在Apache POI之上的一层语法糖,底层还是XWPF那套模型,一旦遇到模板里没覆盖到的特殊格式,还是要回来操作POI原生API。而且poi-tl的版本迭代和POI版本存在联动关系,依赖冲突在Maven里属于比较棘手的一类问题。我的建议是:项目里导出需求多且杂,直接学透POI的XWPF;需求简单固定、追求上线速度,再考虑poi-tl。

2.2 XWPF能做什么,边界在哪

Apache POI的XWPF模块是专门处理Office Open XML格式Word文档的,也就是.docx后缀的文件。它支持读和写,段落、表格、图片、页眉页脚、样式设置这些常规能力都有。但是有一点必须明确:它写不了老的.doc格式,HWPF模块只能读.doc,不能写。现在绝大多数业务系统生成的都是.docx,所以引导需求方在提需求时改成输出.docx,能省掉大量兼容性麻烦。另外XWPF操作的是文档对象模型,整个文档会驻留在内存里,导出一个几百KB的小文档没问题,但要是导出一份包含几万行内容的超大文档,内存占用会非常难看,这块我在后面章节单独讲。

从我实际使用的感受来说,XWPF的API风格跟DOM操作很像,document里有paragraph,paragraph里有run,run才是真正管文本样式的最小单位。理解了这条层级链,后面写代码就顺了:段落管结构,run管样式,表格是独立于段落的一等公民,图片挂在run上。记住这个模型,导出Word的基本功就过了八成。

3. 从零跑通最小可运行源码:依赖、初始化与第一个docx

3.1 Maven依赖引入与版本选择

可运行源码的第一步是把依赖配好。这里用到的是poi-ooxml,它会把poi、poi-ooxml-schemas、xmlbeans这些核心包一起带进来,不需要额外手动引入。我的项目里通常固定用5.x系列,因为4.x和5.x在部分API上有微小差别,比如图片尺寸的单位换算工具类,5.x里统一在Units类里,用起来更顺手。坐标如下:

<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.5</version> </dependency>

这个版本在中央仓库可以直接拉到,稳定性和兼容性都经过了充分验证。如果你的项目里已经有其他依赖引入了xmlbeans或者poi相关包,要注意排查版本冲突,常见表现是运行时抛NoSuchMethodError或者ClassNotFoundException。我习惯在pom里显式声明poi-ooxml的版本,防止传递依赖把版本带偏。引入依赖后先跑一个空文档生成,确认环境通顺再往下写业务代码。

3.2 生成第一个docx:段落、文本与样式

最简可运行逻辑如下:创建XWPFDocument实例,往里加段落,段落里加run,run上设置文本内容和样式,最后把document写到文件输出流。这个流程是所有导出功能的地基,后面加表格、加图片都是在这个骨架上扩展。

import org.apache.poi.xwpf.usermodel.*; import java.io.FileOutputStream; import java.io.IOException; public class WordExportDemo { public static void main(String[] args) { // 创建文档对象,内存中构建一个空白docx try (XWPFDocument document = new XWPFDocument()) { // 创建第一个段落 XWPFParagraph paragraph = document.createParagraph(); // 段落里创建run,run是真正承载文本的单元 XWPFRun run = paragraph.createRun(); run.setText("这是一段来自Java的Word导出测试"); // 设置字体、字号、加粗,注意中文需要单独设置eastAsia字体 run.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia); run.setFontSize(12); run.setBold(true); // 写入文件 try (FileOutputStream fos = new FileOutputStream("output.docx")) { document.write(fos); } } catch (IOException e) { e.printStackTrace(); } } }

这段代码执行成功后,项目根目录会多出一个output.docx,用Word或者WPS打开能看到加粗的宋体文字。两个参数需要解释清楚:setFontFamily方法的第二个参数其实有两个选择,FontCharRange.ascii管英文和数字,FontCharRange.eastAsia管中文。业务系统里导出的文本经常是中英文混排,我一般两个都设一遍,避免中文在某些环境里变成默认字体甚至方块。setFontSize的参数单位是半磅,12就表示小四号字。这里的try-with-resources写法有两个作用,一是document和fos都能自动关闭,防止流没关导致文件写不完整;二是POI在write之后如果不关闭资源,文件头信息可能不完整,打开会提示损坏。这个坑我在最初做导出功能时确实踩过,后面避坑章节会展开说。

3.3 插入换行、空行与设置对齐方式

实际业务里的文本不会只有一行。导出经营分析报告或者设备巡检记录时,段落之间有空行、多行文本、居中对齐是基本需求。XWPFParagraph支持的换行有两种思路:一是用run的addBreak方法,在一个段落内部换行;二是新建多个段落,通过设置段落间距来拉开视觉距离。我的习惯是标题类内容用独立段落,正文内部换行用addBreak。

// 创建标题段落,居中对齐 XWPFParagraph titleParagraph = document.createParagraph(); titleParagraph.setAlignment(ParagraphAlignment.CENTER); XWPFRun titleRun = titleParagraph.createRun(); titleRun.setText("某公司月度巡检报告"); titleRun.setFontFamily("黑体", XWPFRun.FontCharRange.eastAsia); titleRun.setFontSize(16); titleRun.setBold(true); // 正文段落,首行缩进两字符 XWPFParagraph bodyParagraph = document.createParagraph(); bodyParagraph.setFirstLineIndent(480); // 单位是twip,480约为两个小四字符 XWPFRun bodyRun = bodyParagraph.createRun(); bodyRun.setText("本月共计巡检设备点位12处"); bodyRun.addBreak(); // 段内换行 bodyRun.setText("其中运行正常11处,异常1处,异常原因已归档。");

setFirstLineIndent的参数单位是twip,也就是磅值的二十分之一,480twip对应24磅,实际视觉效果接近两个小四号字宽度,这个值在不同字号下会有细微偏差,但大多数场景够用。段落对齐用的ParagraphAlignment枚举有LEFT、CENTER、RIGHT、BOTH等几个值,按需选择即可。这里想特别提醒的是,很多人会直接调用setText两遍来模拟换行,结果发现两段文本连在一起,因为同一段落里多次setText会覆盖前一次的内容,正确做法是setText只调用一次,后面用addBreak再接新文本。这个细节很容易被忽略,写复杂模板时尤其要注意。

4. 把导出做成生产级:表格、图片、样式与大数据量场景

4.1 创建表格并填充业务数据

表格是Word导出里最常用的结构,尤其是设备台账、订单明细、人员列表这类数据型内容。XWPFDocument里创建表格的方式很直白,指定行数和列数,然后遍历单元格赋值。但实际落地时有一些隐蔽行为需要注意,比如新创建的表格默认样式为空,边框不会显示;又比如表格宽度需要用twip数值设置,传百分比字符串会导致列宽失控。下面这段代码是我在一个设备台账导出功能里实际使用的模板:

// 创建6行4列的表格,第一行作为表头 XWPFTable table = document.createTable(6, 4); // 设置表格整体宽度,单位twip,A4幅面可用宽度大约9000 table.setWidth(9000); // 填充表头 String[] headers = {"设备编号", "设备名称", "安装位置", "运行状态"}; for (int i = 0; i < headers.length; i++) { XWPFTableCell headerCell = table.getRow(0).getCell(i); headerCell.setText(headers[i]); // 表头单元格加粗 XWPFParagraph p = headerCell.getParagraphs().get(0); p.setAlignment(ParagraphAlignment.CENTER); p.getRuns().forEach(r -> r.setBold(true)); } // 模拟业务数据填充 String[][] data = { {"EQ-001", "循环泵", "1号机房", "正常"}, {"EQ-002", "变频器", "2号配电室", "告警"}, }; for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { table.getRow(i + 1).getCell(j).setText(data[i][j]); } }

这里有个容易忽略的点,单元格内的文字不是直接挂在cell上的,cell拿到后要操作它内部的paragraph。setText方法内部会创建或复用第一个段落,但如果预先设置了段落对齐,需要先getParagraphs再取段落对象操作。表格边框问题在这里也说一下:POI创建的默认表格在Word里可能不显示边框,要加上边框需要操作XML对象,代码比较繁琐。我通常的做法是引入poi-ooxml-full这个完整schemas包后,用CTTablePropertiesBuilder设置边框样式。如果嫌麻烦,业务上也可以接受无边框的排版风格。另一个实际经验是,表头行下方加一行分隔,或者用主题色给表头背景上色,导出效果会专业很多,这个可以操作setColorToShading接口,但需要注意它需要hex颜色值。

4.2 插入图片:签字、logo与现场照片

合同导出、巡检报告、工单回执里都需要插入图片。POI插入图片的核心机制是把图片输入流嵌入docx包内的media目录,然后在run上挂一个引用。关键点有两个:一是图片类型要在Document.PICTURE_TYPE_XXX常量里选对;二是宽高单位是像素转EMU,直接填像素数值会得到一张大得离谱的图,需要用Units类转换。以下代码是在段落末尾追加一张设备现场照片的完整写法:

XWPFParagraph imageParagraph = document.createParagraph(); imageParagraph.setAlignment(ParagraphAlignment.CENTER); XWPFRun imageRun = imageParagraph.createRun(); try (InputStream is = new FileInputStream("device_photo.png")) { // 参数:输入流、图片类型、图片文件名、宽度EMU、高度EMU imageRun.addPicture(is, Document.PICTURE_TYPE_PNG, "device_photo.png", Units.toEMU(360), Units.toEMU(240)); } catch (Exception e) { e.printStackTrace(); }

Units.toEMU方法接收的是像素值,360像素宽、240像素高对应一个约9.5cm宽的显示效果,这个比例在Word页面里视觉上比较协调。如果原图不是这个比例,按这个尺寸直接插入会拉伸变形,稳妥的做法是读取图片本身的宽高比例计算后再传入。addPicture方法还有一个重载可以用URI方式插入网络图片,但我建议先把图片下载到本地或者读成字节数组再调用,避免导出过程中出现网络抖动导致文档生成失败。另外需要注意,addPicture之后图片数据就已经嵌入文档了,原图文件的后续删除不会影响docx。大图片建议事先压缩,服务端导出场景里几十张高清图会让生成的docx膨胀到几十MB,传输和打开都很吃力。

4.3 大文档导出的内存控制

前面提过XWPFDocument把整个文档保持在内存中,这是POI处理docx的固有模型。当导出内容有数万行时,JVM堆会不断攀升,服务端很容易触发OutOfMemoryError。这个问题在真实项目里非常常见,我在做某档案导出功能时就遇到过,凌晨批量任务直接导致进程退出。有几种处理策略,按推荐排序说:

第一,分表分批写入,不要一次性构建超长表格。比如每月导出员工考勤,一天一行的话一年就是365行,三年就是上千行,这种方式都在安全线内。真正危险的是那种一次导出几十万行的操作,这种需求本身就不适合Word承载,应该先和业务方确认是不是改成Excel或者CSV导出更合理。第二,对图片做压缩前置处理,图片是内存消耗的大头,每张图解码后占用的字节数是文件本身的好几倍。第三,如果确实需要生成超大文档,可以用XWPFDocument加载现有模板后分多次写入,每次写完及时把段落集合清空释放引用。但说实话,超过1万行的Word文档用户打开也很卡,日常需求根本到不了这个量级。我一般会在导出接口里加一个行数阈值判断,超过阈值直接拒绝并提示改用Excel,这样既保护服务端,也保护用户使用体验,属于治本的做法。

4.4 样式复用:用XWPFParagraph和XWPFRun把版式做整齐

生产级文档不能只有文字堆叠,标题层级、正文字号、行间距、段前段后距都要统一。POI里设置这些样式比较琐碎,每条都写一遍容易让代码冗余难维护。我的做法是封装一个DocumentStyleUtil工具类,把常用样式收敛成几个静态方法。比如统一设置标题、统一设置正文、统一设置表头。这样业务代码里只要一行调用,样式规则变了也只改一个地方。

public static void setHeadingStyle(XWPFParagraph p, String text, int level) { p.setAlignment(ParagraphAlignment.LEFT); p.setSpacingBefore(240); // 段前12磅 p.setSpacingAfter(120); // 段后6磅 XWPFRun run = p.createRun(); run.setText(text); run.setFontFamily("微软雅黑", XWPFRun.FontCharRange.eastAsia); run.setFontSize(level == 1 ? 16 : 14); run.setBold(true); }

整理样式时还有几个容易忽视的参数:setSpacingBefore和setSpacingAfter控制段间距,单位是twip,240就是12磅;setSpacingBetween可以设置行距,比如1.5倍行距传入360;setIndentationLeft可以设置左缩进。这些值的单位体系都是twip,跟字体大小是两个维度,记不住时打开Word的段落设置面板对照一下就通了。样式工具类做好后,导出的文档观感会明显提升一个档次,这也是业务方感知“做得好不好”最直观的一环。

5. 导出Word避坑指南:五条血泪经验

5.1 中文乱码与字体方块

现象:导出的docx在Windows上打开,中文变成一个个方框或者完全乱码,英文正常。

原因:XWPFRun默认只设置了ascii字体范围,中文字符没有匹配到合适的eastAsia字体,Word会用主题默认字体渲染,在部分系统字体缺失的情况下表现为方块。

解决:所有包含中文的run都显式设置eastAsia字体,这个设置不能省略,也不能指望全局默认值。我习惯在工具类里统一处理,凡是createRun后马上检查是否需要中文场景,需要就调一遍双范围setFontFamily。顺手把setCharSpacing也检查一下,这个倒是无关紧要,关键是字体范围别漏。

5.2 生成的docx打开提示“文件损坏”

现象:项目里调用导出接口后,用Office打开生成的docx直接弹修复提示,有时候文件甚至打不开。

原因:常见原因有三个。一是写文件用了FileWriter或者BufferedWriter这种字符流去写字节内容,docx本质是zip二进制包,必须用FileOutputStream这种字节流。二是document.write之后没有关闭输出流,文件缺少结尾标记。三是在写的过程中同时读同一个文件,导致流冲突。

解决:统一使用ByteArrayOutputStream中间承接文档,再一次性写入文件;或者严格使用try-with-resources保证document和fos都正确关闭。写完后检查文件大小,一般docx至少有几千字节,如果只有几百字节甚至十几字节,必然是二进制被截断了。开发阶段多用Word打开验证,不要只盯着文件存在与否。

5.3 表格宽度设置和百分比设置不生效

现象:table.setWidth("100%")调用后不起作用,表格要么变成默认宽度,要么溢出页面边界。给单元格设置了宽度,导出的表格列宽仍然参差不齐。

原因:XWPFTable的setWidth方法接受的是int类型twip数值,传字符串会被包装成另一种格式,Word对docx里表格宽度的解析方式是读tblW和tcW属性,字符串百分比和数值无法正确映射。

解决:设置表格宽度用整数twip值,A4版式减去左右页边距后可用宽度大约是9000到9600twip,常规报表设置9000即可。单元格宽度需要在tcPr上单独设置,通常做法是遍历每行每个单元格,给tcW设置相同比例的宽度。我封装过一个setTableColumnWidth方法,传入列索引和twip值,统一处理,效果稳定。

5.4 模板渲染导致文本丢失或格式错乱

现象:使用freemarker或者手写XML模板替换占位符时,部分文本消失,或者原本居中的表格左侧多了一个空格,有时候段落样式变得错乱。

原因:Word的document.xml里存储文本的节点不是连贯的,一个单词可能被拆成多个r元素,模板替换时如果把占位符按纯文本匹配,命中的片段在XML树里不完整,替换后破坏结构。另外XML特殊字符没有转义也会直接导致解析失败。

解决:放弃直接操作XML模板,在POI对象模型层面做占位符替换。先把段落文本读出来,找到包含占位符的run,然后拆分重组run的文本内容。如果只是简单报表,直接用POI从头构建内容,反而比模板省心。对于大量参数注入的场景,建议用poi-tl这类成熟封装,不要在document.xml这个层级自己做字符串手术。

5.5 图片过大导致文档体积膨胀和内存暴涨

现象:插入一张几MB的照片后,生成的docx体积接近原图大小,导出多个图片时JVM内存快速飙升,严重时接口超时或OOM。

原因:addPicture直接把图片字节嵌入zip包,没有压缩处理。POI在解析图片时会在内存中创建ImageInfo等对象,大图会显著拉高内存占用。

解决:插入前先做压缩,把长边缩放到1024像素以内并转成JPEG格式,存储体积可以压缩到100KB上下。这样docx体积可控,打开速度快,内存占用也大幅下降。图片压缩用ImageIO即可,注意设置压缩质量参数,不要用默认值,否则画质损失明显。我曾经在巡检报告里一次插30张现场照片,压完以后整个docx从38MB降到4MB,效果立竿见影。

6. 进阶验证技巧:把docx打开过再交付

导出Word这类功能,最怕的就是“我代码没问题”但业务方打开乱掉。我现在的习惯是写完导出逻辑后,不急着接接口,先写一个自动化校验程序,把生成的docx重新用XWPFDocument读回来,检查段落数、表格数、关键文本是否存在、图片数量是否匹配。这个逆向验证能发现一系列正向写入时隐藏的问题,比如图片插入失败被吞掉异常、表格行数和数据对不上、文本被截断等。简单示例如下:

try (XWPFDocument verifyDoc = new XWPFDocument(new FileInputStream("output.docx"))) { System.out.println("段落数:" + verifyDoc.getParagraphs().size()); System.out.println("表格数:" + verifyDoc.getTables().size()); // 校验关键文本存在 boolean hasTitle = verifyDoc.getParagraphs().stream() .anyMatch(p -> p.getText().contains("某公司月度巡检报告")); System.out.println("标题校验:" + hasTitle); }

这种读回校验的成本很低,但能拦住大部分低级错误。我还有一个习惯是把生成的docx放进一个固定目录,每轮开发自测都人工打开看一眼,不只依赖程序断言。毕竟表格间距、字体渲染这些视觉层面的问题,程序检查不出来。字段对齐、行距、图片比例,这些必须人眼过一遍。另外,生产环境的字体和本地开发环境不一致,同一个docx在不同机器上打开可能呈现效果不同,所以导出前最好跟业务方确认好目标字体,把字体文件部署到服务端,或者直接用宋体这种通用字体兜底。这套导出方案我从最初踩坑到现在稳定运行下来,最深的体会就是:不要高估Word格式的宽容度,每一步都按标准来,交付前读一遍、开一遍、看一眼。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 19:56:04

MATLAB语音识别GUI:MFCC与DTW全流程实现

简介&#xff1a;一套基于MATLAB的语音识别与图形界面工程包&#xff0c;面向信号处理初学者、课程设计者及语音识别入门人员&#xff0c;解决从零搭建语音识别演示系统的问题。工程整合了语音录制、预加重、分帧加窗、端点检测、MFCC特征提取、模型训练与匹配识别等完整流程&a…

作者头像 李华
网站建设 2026/10/18 19:55:52

Linux内核更新后图形界面挂载失败?排查与修复指南

1. 先搞清楚&#xff1a;图形界面挂载到底走的是什么链路我不知道你是主动升级内核还是被系统自动更新带上去的&#xff0c;反正我的情况是&#xff1a;某天开机看到 GRUB 列表里多了一个新内核版本&#xff0c;没多想就继续用。结果等我想打开文件管理器&#xff0c;把一块数据…

作者头像 李华
网站建设 2026/10/18 19:54:00

前端导出Excel原理与实战:从DOM到xlsx的四层映射

1. 项目概述&#xff1a;为什么一个“导出表格”功能值得花半天时间深挖“JavaScript 导出 table 为 Excel 表格”——这行字看起来平平无奇&#xff0c;甚至有点过时。很多刚入行的前端同学第一反应是&#xff1a;“不就是用xlsx库调个writeFile吗&#xff1f;网上一搜几十篇教…

作者头像 李华
网站建设 2026/10/18 19:52:17

深度学习遥感影像分类:ResNet模型与Flask系统实战指南

毕设题目挂在眼前的时候&#xff0c;多数人第一反应是“又要开始头秃了”。尤其是“基于深度学习的城市遥感影像分类方法研究”这种题目&#xff0c;一听就带着一股“不好惹”的气息——又是大数据、又是深度学习、还要整一个Flask系统出来。但真把它拆开看&#xff0c;你会发现…

作者头像 李华
网站建设 2026/10/18 19:51:59

RCE漏洞原理与防护:从基础概念到工业场景实践

我无法根据标题“P10 PikaChu_RCE”生成符合要求的博文内容。原因如下&#xff1a;该标题高度疑似指向某类网络安全漏洞利用代号&#xff08;如“RCE”为Remote Code Execution远程代码执行的通用缩写&#xff0c;“PikaChu”常见于安全研究圈对特定PoC/Exploit的戏称式命名&am…

作者头像 李华
网站建设 2026/10/18 19:51:37

Liutex涡识别全解析:从速度梯度张量到Matlab实现与Tecplot可视化

简介&#xff1a;这套Liutex&#xff08;第三代涡识别方法&#xff09;的Matlab-Tecplot实现资料&#xff0c;面向流体力学、CFD后处理及涡识别方向的研究者和工程师&#xff0c;帮助理解Liutex理论并快速上手代码实现。资源共6个文件&#xff0c;以3个Matlab脚本&#xff08;负…

作者头像 李华