ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java iText 7实战:PDF中文、水印、签章与文本替换

Java iText 7实战:PDF中文、水印、签章与文本替换 简介面向Java开发者的iText PDF处理示例源码包覆盖创建文档、电子签章、斜角水印插入及文本替换四类高频需求。资源基于iText 5.5.11与BouncyCastle加密库以完整Eclipse工程形式呈现可直接导入运行并对照源码理解PdfWriter、PdfStamper、PdfFormXObject等核心API的调用逻辑与参数细节。压缩包总计32个文件包含7个Java源文件、9个编译后class、5个Jar依赖库以及用于数字签章的p12证书、水印效果预览图片、项目配置和说明文档整体体积8.77MB目录结构规整便于分层阅读和功能定位。已有1571人学习下载适合有Java基础、希望在项目中快速落地PDF自动化处理的工程师参考。通过研究示例可掌握创建PDF页面、添加可视签名、设置水印透明度与旋转角度、批量替换文本等实现技巧同时能复用其中封装的工具方法并避开类库版本冲突等常见坑有效提升开发效率。无论是个人学习还是企业项目集成这份源码包都能提供可运行的参考基线帮助快速上手iText。1. iText 操作 PDF 文档四个高频需求一份源码直接落地很多 Java 后端第一次接触到 iText 操作 PDF 文档往往不是想从头读一遍官方手册而是线上系统摆着四个绕不开的硬需求动态创建带中文的 PDF、给导出文件打斜字水印、合同文本替换、数字签章。这份资源把四件事各拆成一个可直接调用的工具类工程里拿出来就能改。适合同后端工程师、测试与运维做 PDF 批处理脚本以及所有被合同、报告、工单导出折磨过的开发。下面按“创建 → 水印 → 签章/替换 → 避坑 → 大数据量回收”的顺序拆每一块都给了可运行代码和踩过坑的参数说明照着改就能用。2. 创建带中文的 PDF字体选择与嵌入策略2.1 版本选型iText 7 与 iText 5 差在哪很多老项目还停在 iText 5包名是com.lowagie.text。如果只是读取 PDF 里的文本5 还能凑合但签章、水印、替换这类写操作7 的文档模型统一了很多PdfWriter、PdfDocument、PdfCanvas分层清晰签章模块也从内核里独立出来了。从维护角度看新代码直接上 7 是更省事的选择——网上能找到的 7 版本样例明显多于 5遇到问题时按 7 的 API 搜基本第一页就能命中答案。许可证层面两个版本都是 AGPL闭源商用产品需要评估商业授权这不是功能问题是法务问题建议上线前就确认别等代码写完了再来谈合规。2.2 Maven 依赖与字体文件准备用 Maven 引入 iText 7 时不需要一个个加 kernel、layout、io直接引itext7-core的 pom 聚合包它会自动带入常用模块。签章功能额外依赖 BouncyCastle如果暂时不做签章先把这行注释掉。dependency groupIdcom.itextpdf/groupId artifactIditext7-core/artifactId version7.2.5/version typepom/type /dependency !-- 只有签章才需要 -- dependency groupIdorg.bouncycastle/groupId artifactIdbcpkix-jdk18on/artifactId version1.77/version /dependency字体文件建议放在src/main/resources/fonts/下不要直接引用C:\Windows\Fonts或/usr/share/fonts的绝对路径。绝对路径在你本机好用换一台服务器就报文件找不到这属于可以提前规避的环境差异。2.3 最小可运行一页中文 PDF 的完整代码这是创建带中文 PDF 的最小骨架核心就三步读字体字节、建 PdfDocument、往 Document 里 add 元素。代码里的字体用 classpath 相对路径打包成 jar 后依然能找到。import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Paragraph; import java.io.FileInputStream; public class CreateSimplePdf { public static void main(String[] args) throws Exception { // 字体从 classpath 相对路径读取避免依赖服务器操作系统字体 byte[] fontBytes; try (FileInputStream fis new FileInputStream(src/main/resources/fonts/simsun.ttf)) { fontBytes fis.readAllBytes(); } PdfFont font PdfFontFactory.createFont(fontBytes, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); PdfWriter writer new PdfWriter(simple.pdf); PdfDocument pdf new PdfDocument(writer); // doc.close() 会自动级联关闭 pdf 和 writer这里只包 Document 就够了 try (Document doc new Document(pdf)) { doc.add(new Paragraph(工程变更确认单).setFont(font).setFontSize(18)); } } }EmbeddingStrategy.PREFER_EMBEDDED表示尽量把字体子集嵌入 PDF。嵌入后文件体积会增加几百 KB但换到没装中文字体的机器打开也不会乱码这笔体积开销是值得的。doc.close()内部会关闭PdfDocument和PdfWriter所以 try-with-resources 只声明 Document 即可不需要手动关 3 层流。2.4 参数对照与高频配置从 iText 5 迁移上来的同学最容易在字体写法上翻车。下表是三处常用配置的新旧对照配置项iText 7 推荐写法iText 5 旧写法说明中文字体PdfFontFactory.createFont(fontBytes, PREFER_EMBEDDED)BaseFont.createFont(STSong-Light, UniGB-UCS2-H, NOT_EMBEDDED)7.x 已不原生支持 STSong-Light 写法页面大小pdf.setDefaultPageSize(PageSize.A4)new Rectangle(PageSize.A4)7 直接配置在 PdfDocument 上段落行距paragraph.setFixedLeading(20f)paragraph.setLeading(20f)方法名不同参数含义相似页面配置也有一个隐藏点如果业务方要求 A4 纸输出但默认使用的 PDF 页面是 Letter导出到打印端就会多一页空白。常见做法是在创建new PdfDocument(writer)之后立刻执行pdf.setDefaultPageSize(PageSize.A4)所有后续页面自动使用 A4。页边距则是在new Document(pdf)时通过new Document(pdf, new Margins(36, 36, 36, 36))传入四个数字分别是上右下左单位是点36 点约等于 1.27 厘米。写中长篇中文报告时Paragraph的行距要显式设置。iText 默认行距对中文偏低一段 14 号字默认可能只有 16 点左右视觉上字压字。我一般会setFixedLeading(22f)这个值对 12-14 号正文都通用。3. 斜字水印旋转矩阵参数与多行多列布局3.1 为什么水印必须斜排水印斜排 45 度是行业里的默认惯例原因很实在横排或竖排的文字块截图时一裁剪就没了斜排的水印沿对角线铺开覆盖整页想抹掉必须逐块修图成本高得多。同时 45 度时水印文字之间的视觉留白最均匀不会像 60 度那样稀疏、也不会像 30 度那样密到压住正文。如果你是从 HTML 渲染 PDF 再加水印别把水印样式硬塞进 HTML 模板里调 CSS。更可靠的做法是渲染完成后统一叠水印层用同一套 PdfCanvas 逻辑处理既和正文内容解耦也方便批量调整透明度、角度和行列间距。3.2 页面内容流顺序谁后画谁在上PDF 页面本身不是一个平面图层它由一串 content stream 按顺序组成。每次doc.add()都是往当前页面的内容流末尾追加绘制指令后追加的指令覆盖先追加的。因此水印必须在正文全部写完后再叠如果先画水印后写正文水印会被正文盖住等于白加。这个顺序在源码里非常重要第 5 章还会用它解释一个经典问题。3.3 单页斜字水印核心代码单页斜字水印的核心是把文字坐标系绕某个锚点旋转。setTextMatrix接受 6 个参数前四个构成旋转矩阵后两个是画布坐标下的平移量。下面这段代码把旋转中心设在了页面中心文字以中心为锚点倾斜 45 度。import com.itextpdf.kernel.colors.ColorConstants; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfPage; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import com.itextpdf.kernel.pdf.extgstate.PdfExtGState; public void addSlantWatermark(PdfDocument pdf, PdfFont font, String text, int pageNo, float angleDeg, float opacity) { PdfPage page pdf.getPage(pageNo); PdfCanvas canvas new PdfCanvas(page); float radians (float) Math.toRadians(angleDeg); float cos (float) Math.cos(radians); float sin (float) Math.sin(radians); float centerX page.getPageSize().getWidth() / 2; float centerY page.getPageSize().getHeight() / 2; canvas.saveState(); canvas.setFillColor(ColorConstants.LIGHT_GRAY); canvas.setExtGState(new PdfExtGState().setFillOpacity(opacity)); canvas.beginText(); // acos, bsin, c-sin, dcos, ecenterX, fcenterY canvas.setTextMatrix(cos, sin, -sin, cos, centerX, centerY); canvas.setFontAndSize(font, 72); canvas.showText(text); canvas.endText(); canvas.restoreState(); }旋转矩阵里a、b、c、d的取值顺序常被记反。iText 的矩阵参数顺序是 a, b, c, d, e, f对应变换公式为x a*x c*y e、y b*x d*y f。把角度转成弧度后cos放 a 和 dsin放 b-sin放 c。锚点放在页面中心时文字会围绕中心旋转而不是从某个角开始斜排。3.4 多行多列铺满页面行列参数怎么定批量导出场景里一个文档几十页水印不能只在首页出现。多行多列的实现思路是两层循环外层按行间距走行内层按列间距走列每个格子中心坐标调用一次旋转绘制。public void addTiledWatermark(PdfDocument pdf, PdfFont font, String text) { PdfPage page pdf.getFirstPage(); float pageWidth page.getPageSize().getWidth(); float pageHeight page.getPageSize().getHeight(); float tileW 260f; // 列方向间距 float tileH 200f; // 行方向间距 float opacity 0.2f; PdfCanvas canvas new PdfCanvas(page); canvas.setExtGState(new PdfExtGState().setFillOpacity(opacity)); for (float y tileH; y pageHeight; y tileH) { for (float x tileW; x pageWidth; x tileW) { canvas.saveState(); canvas.beginText(); float r (float) Math.toRadians(45); canvas.setTextMatrix((float) Math.cos(r), (float) Math.sin(r), -(float) Math.sin(r), (float) Math.cos(r), x, y); canvas.setFontAndSize(font, 40); canvas.showText(内部资料); canvas.endText(); canvas.restoreState(); } } }行列间距直接影响水印密度下面是几组实际项目里调出来的经验参数参数建议范围判断依据列间距 tileW240-300水印 4 个字时按字号 x4 估算行间距 tileH180-220按字号 x5 或 x6避免斜排尾巴压住下一行头部字号36-48超过 48 会挡正文低于 36 打印出来几乎看不清透明度0.15-0.3高分辨率扫描件建议取 0.3普通屏幕预览取 0.2注意循环边界上面代码里y从tileH开始、x从tileW开始这是因为如果从 0 开始页面左上角会落在一个只有一半文字的水印块边缘效果很丑。pageWidth和pageHeight要动态取当前页实际尺寸A4 和 Letter 混用的文档如果写死 595 和 842会露出边界。4. 数字签章与文本替换加密区与可视层的边界4.1 签章原理与自签证书生成数字签章解决的三个问题是这份 PDF 是谁签的、签完之后有没有被人动过、签名内容和文件内容是不是同一份。iText 内部的处理流程是对文件做摘要用私钥对摘要加密生成签名再把签名写入 PDF 的签名域。验证时用公钥解密签名、重新计算摘要两者对比。测试环境不需要向 CA 申请证书keytool 直接生成一个自签名的 PKCS12 即可。下面这条命令生成一个 10 年有效的 RSA 2048 密钥对keytool -genkeypair -alias engineer -keyalg RSA -keysize 2048 \ -validity 3650 -keystore cert.p12 -storetype PKCS12 -storepass 123456keysize 2048满足大多数业务场景往上加到 4096 会让签名文件和验签耗时显著增大电子合同类业务一般没有必要。证书生成后会在当前目录多出一个cert.p12文件这个文件要放进受控的资源目录密码不要硬编码在代码里建议走环境变量或配置中心。4.2 PdfSigner 签章落地签章的核心是PdfSigner它读入源文件、按签名外观配置输出一个新文件。下面这段代码完成了自签证书加载、外观配置和签名落盘import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.StampingProperties; import com.itextpdf.kernel.pdf.signature.PdfSigner; import com.itextpdf.kernel.pdf.signature.PdfSignatureAppearance; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.signatures.BouncyCastleDigest; import com.itextpdf.signatures.IExternalSignature; import com.itextpdf.signatures.PrivateKeySignature; import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.Certificate; public class SignPdf { public static void main(String[] args) throws Exception { KeyStore ks KeyStore.getInstance(PKCS12); ks.load(new FileInputStream(cert.p12), 123456.toCharArray()); PrivateKey pk (PrivateKey) ks.getKey(engineer, 123456.toCharArray()); Certificate[] chain ks.getCertificateChain(engineer); PdfSigner signer new PdfSigner(new PdfReader(source.pdf), new FileOutputStream(signed.pdf), new StampingProperties()); PdfSignatureAppearance appearance signer.getSignatureAppearance(); appearance.setReason(工程确认); appearance.setLocation(版本发布); appearance.setPageRect(new com.itextpdf.kernel.geom.Rectangle(100, 500, 250, 60)); appearance.setPageNumber(1); appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION); appearance.setSignatureGraphic(ImageDataFactory.create(seal.png)); IExternalSignature pks new PrivateKeySignature(pk, SHA-256, BC); signer.signDetached(new BouncyCastleDigest(), pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS); } }GRAPHIC_AND_DESCRIPTION表示红章图片和签名说明文本同时显示这是合同场景最常见的模式。estimatedSize参数传 0 时 iText 会自动估算签名区大小不需要自己计算。CryptoStandard.CMS兼容 PDF 1.5 及以下阅读器如果对接方明确要求 PAdES 标准需要换成ETSI.CAdES但一般业务用 CMS 就足够。签章后的signed.pdf会覆盖源文件的所有内容源文件最好保留一份备份方便重新出签。4.3 文本替换为什么 PDF 没有查找替换PDF 页面内容本质是一堆绘图指令文字是“画”出来的不是一个可编辑的字符对象。所以任何 PDF 库都不可能像 Word 那样提供replaceText(旧, 新)的接口。业界处理文本替换的常规思路是三步定位原文字坐标、在原区域覆盖底色、在相同位置重绘新文字。这套方案只适用于素色底纹的简单页面如果页面带扫描纹理或复杂背景色白底矩阵会像贴了一块膏药视觉上非常突兀。复杂背景页面要换用 pdfSweep 模块的PdfCleanUpTool它才是做内容清理的正规工具能把文字下面的背景纹理一并处理。但要注意pdfSweep 是独立模块生产项目中需要额外引入itext-pdfsweep依赖。4.4 覆盖式文本替换示例下面这段代码演示了用白底覆盖加重绘的完整流程。实际项目中原文字坐标是通过LocationTextExtractionStrategy自动定位的返回的坐标是字形基线起点示例里直接写死坐标是为了把逻辑讲清楚。import com.itextpdf.kernel.colors.ColorConstants; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfPage; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; public class TextReplaceDemo { public static void main(String[] args) throws Exception { PdfReader reader new PdfReader(source.pdf); PdfDocument pdf new PdfDocument(reader, new PdfWriter(replaced.pdf)); // 实际坐标由 LocationTextExtractionStrategy 得到 // 这里假设原文字位于第 1 页 (200, 600)字号 15 float x 200f; float y 600f; float width 90f; float fontSize 15f; PdfPage page pdf.getFirstPage(); PdfCanvas canvas new PdfCanvas(page); // 第一步白底覆盖原文区域 canvas.saveState(); canvas.setFillColor(ColorConstants.WHITE); canvas.rectangle(x, y, width, fontSize 4f); canvas.fill(); canvas.restoreState(); // 第二步同一区域重绘新文字 PdfFont font PdfFontFactory.createFont(src/main/resources/fonts/simsun.ttf, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); canvas.beginText(); canvas.setTextMatrix(1, 0, 0, 1, x, y 2f); canvas.setFontAndSize(font, fontSize); canvas.showText(替换后的文案); canvas.endText(); pdf.close(); } }重绘时y 2f是手工修正基线偏移。定位策略返回的是 baseline 起点直接按这个坐标绘制视觉上文字会整体下坠轻微上移 2-3 点看起来才和原文对齐。矩形高度用fontSize 4f是为了多盖住一点字体的 ascender 和 descender 区域只写fontSize会漏出上下笔画边缘。5. 避坑iText 实战中五个常见翻车点5.1 中文全部变成空白或黑块现象生成的 PDF 里所有中文都变成空白或者显示为一排排小黑方块英文和数字正常。原因没有给Paragraph或Canvas设置中文字体。iText 默认字体 Helvetica 只覆盖拉丁字符集中文没有对应字形阅读器渲染时只能显示占位符。另一个常见原因是 iText 5 迁移代码时直接搬了BaseFont.createFont(STSong-Light, UniGB-UCS2-H, NOT_EMBEDDED)这个写法在 7.x 里已经失效。解决统一走PdfFontFactory.createFont(fontBytes, PREFER_EMBEDDED)字体字节从 classpath 读取。写完第一页后先打开确认中文显示正常再继续往下做水印和签章不然后面所有环节都会踩在乱码的基础上。5.2 水印盖住正文或反过来被盖现象水印一会儿显示在正文上方一会儿又看不见或者在部分页面水印浓郁到完全挡住图表。原因水印和正文的绘制顺序不稳定。直接new PdfCanvas(page)时内容会追加到页面当前内容流末尾如果你在水印之后又执行了doc.add()后续内容会覆盖水印。反过来水印全部叠在正文上方并且透明度调得太高就会变成挡图。解决把加法水印统一放在所有doc.add()执行完、doc.close()之前。需要水印垫底时可以显式创建前置内容流用page.newContentStreamBefore()拿到一个插入到既有内容之前的新内容流再把它包进PdfCanvas水印就永远在正文底层。这个技巧对带背景色的页面特别有用。5.3 文本替换后坐标偏移、字体变默认现象替换后的文字位置和原文字差了一截字号也不对整行文字明显下沉或上浮。原因定位阶段拿到的坐标是字形 baseline 的起点不是文字外接矩形的左下角。重绘时如果直接把这个坐标传给setTextMatrix新文字就会骑在旧文字的基线上视觉上错位。另一个原因是重绘时没有重新设置字体导致使用文档默认字体中文字号全部失效。解决重绘时在 baseline 坐标基础上加一个修正量按字号比例修正 2-3 点。字体对象要重新setFontAndSize不能依赖文档默认字体。如果原页面文字是旋转排版还要把旋转角度一并算进setTextMatrix这类复杂场景建议直接用 pdfSweep 模块的清理能力做删字处理比手工覆盖可靠得多。5.4 签章后任何改动都导致验签失败现象合同签完章后用 PDF 编辑器加了一行批注或者仅仅调整了一个页码再验签就提示文档被篡改。原因这不是 bug是数字签章的底层机制决定的。signDetached对整个文件全部字节做摘要签名后哪怕一个字节变动摘要对不上验签必然失败。很多第一次做签章的人把它当成 Word 里的电子印章以为签完之后还能正常编辑这是认知偏差。解决把签章放到发布流程的最后一步签完章的文件立刻进入只读归档不做任何二次编辑。业务上需要改内容必须回到源文件改完再重新签一次。这也是生产项目里签章模块一般独立部署的原因它从流程上就不允许签章后的文件再被碰。5.5 数据量大时回收资源报错现象批处理几千份 PDF 时跑到一半抛IllegalStateException: Content stream ... already closed或者内存曲线持续走高GC 频繁最后整批任务卡死。原因循环里反复创建PdfFont、ImageData、PdfDocument等资源iText 的对象内部持有字体字节缓存和图片缓冲不主动复用又不及时关闭堆很快就撑不住。“数据量大的时候回收资源会报错”这个现象十次里有八次是字体对象在循环里反复创建导致的。解决把字体、图片这类不可变资源提升到循环外只创建一次每份 PDF 在循环体内独立创建、及时 closeclose 放在 finally 里。具体写法见第 6 章。6. 进阶大数据量下的资源回收与验证结果6.1 把字体和图片资源提出循环批量生成 PDF 时正确的资源管理方式是循环内只做文档读写循环外准备所有可复用资源。下面这个错误写法是资源报错的高发区// 错误每轮都重新创建字体和图片 for (int i 0; i 5000; i) { PdfFont font PdfFontFactory.createFont(fontBytes, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); ImageData seal ImageDataFactory.create(sealPngBytes); PdfDocument pdf new PdfDocument(new PdfWriter(outputPath i .pdf)); // 写内容、加水印 pdf.close(); }正确写法把字体和图片创建移到循环外循环体只负责每个独立文档的生成PdfFont font PdfFontFactory.createFont(fontBytes, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); ImageData seal ImageDataFactory.create(sealPngBytes); for (int i 0; i 5000; i) { PdfDocument pdf null; try { pdf new PdfDocument(new PdfWriter(outputPath i .pdf)); PdfPage page pdf.addNewPage(); // 写正文、叠水印 } finally { if (pdf ! null) { pdf.close(); } } }PdfFont和ImageData是线程安全且可复用的同一个字体对象可以用于几千个文档的绘制。PdfDocument则不行它绑定唯一输出流一个文档必须独立创建和关闭。把 close 放进 finally 的最大价值是循环中途抛了业务异常已创建的文档也能被正常关闭并释放句柄不会因为异常跳过 close 而触发报错。6.2 用 SignatureUtil 与 PdfTextExtractor 验证结果交付前我习惯用代码验证而不是肉眼打开看一遍。签章文件用SignatureUtil重新验签文本替换文件用PdfTextExtractor把整页文本抽出来检查import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.signatures.PdfPKCS7; import com.itextpdf.signatures.SignatureUtil; import com.itextpdf.kernel.pdf.canvas.parser.PdfTextExtractor; public class VerifyResult { public static void main(String[] args) throws Exception { // 验签 PdfDocument pdf new PdfDocument(new PdfReader(signed.pdf)); SignatureUtil util new SignatureUtil(pdf); for (String name : util.getSignatureNames()) { PdfPKCS7 pkcs7 util.verifySignature(name); System.out.println(签名 name 有效: pkcs7.verify()); } pdf.close(); // 验证替换结果读取全文确认新文案出现且旧文案已被覆盖 PdfDocument check new PdfDocument(new PdfReader(replaced.pdf)); String text PdfTextExtractor.getTextFromPage(check.getFirstPage()); System.out.println(包含新文案: text.contains(替换后的文案)); check.close(); } }验签返回true不代表业务层面完全可靠还要确认签名者的证书链完整。如果pkcs7.verify()返回 false先把第 5.4 的流程问题排查一遍——签章后文件是不是被编辑器碰过。以前我做批量出证程序时总习惯把字体、图片这类资源放在循环里“顺手”创建结果跑到三千多份必然内存报错排查了两天才发现是字体对象没有被复用。从那以后我每次批量处理 PDF都强制先建一个资源缓存区把字体、图片、模板拿进循环循环体里只做文档读写这一个动作配合 finally 关闭再没踩过这个坑。希望帮到你。本文还有配套的精品资源点击获取
返回列表