给导出的excel添加水印

覆盖图片式添加水印

此方式是根据覆盖图片形式进行的水印的添加,缺点是在可以修改情况下,覆盖的图片会影响修改操作。

1.创建水印图片

 /*** 创建水印图片* 注意:生成的图片会默认保存到classes目录下,可以根据自己的业务进行更改* @param content* @param fileName* @return* @throws IOException*/private static String createWaterMark(String content, String fileName) {//生成水印图片的宽度Integer width = 700;//水印图片的高度Integer height = 300;// 获取bufferedImage对象BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);String fontType = "宋体";Integer fontStyle = java.awt.Font.PLAIN;Integer fontSize = 50;java.awt.Font font = new java.awt.Font(fontType, fontStyle, fontSize);// 获取Graphics2d对象Graphics2D g2d = image.createGraphics();image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);g2d.dispose();g2d = image.createGraphics();g2d.setColor(new java.awt.Color(0, 0, 0, 80)); //设置字体颜色和透明度// 设置字体g2d.setStroke(new BasicStroke(1));// 设置字体类型  加粗 大小g2d.setFont(font);//设置倾斜度g2d.rotate(Math.toRadians(-10), (double) image.getWidth() / 2, (double) image.getHeight() / 2);FontRenderContext context = g2d.getFontRenderContext();Rectangle2D bounds = font.getStringBounds(content, context);double x = (width - bounds.getWidth()) / 2;double y = (height - bounds.getHeight()) / 2;double ascent = -bounds.getY();double baseY = y + ascent;// 写入水印文字原定高度过小,所以累计写水印,增加高度g2d.drawString(content, (int) x, (int) baseY);// 设置透明度g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));// 释放对象g2d.dispose();//自定义存放位置String targetImagePath ="F:\\data\\erp\\"+fileName;//默认存放位置// String s = Thread.currentThread().getContextClassLoader().getResource("").getPath() + fileName;try {ImageIO.write(image, "png", new File(targetImagePath));} catch (IOException e) {e.printStackTrace();}return targetImagePath;}

2. 给excel加水印

  /*** 为Excel打上水印工具函数 请自行确保参数值,以保证水印图片之间不会覆盖。在计算水印的位置的时候,并没有考虑到单元格合并的情况,请注意* @param wb Excel Workbook* @param sheet 需要打水印的Excel* @param waterRemarkPath 水印地址,classPath,目前只支持png格式的图片,*                        因为非png格式的图片打到Excel上后可能会有图片变红的问题,且不容易做出透明效果。*                        同时请注意传入的地址格式,应该为类似:"\\excelTemplate\\test.png"* @param startXCol 水印起始列* @param startYRow 水印起始行* @param betweenXCol 水印横向之间间隔多少列* @param betweenYRow 水印纵向之间间隔多少行* @param XCount 横向共有水印多少个* @param YCount 纵向共有水印多少个* @param waterRemarkWidth 水印图片宽度为多少列* @param waterRemarkHeight 水印图片高度为多少行* @throws IOException*/private static void putWaterRemarkToExcel(Workbook wb, Sheet sheet, String waterRemarkPath, int startXCol,int startYRow, int betweenXCol, int betweenYRow, int XCount, int YCount, int waterRemarkWidth,int waterRemarkHeight) throws IOException {// 校验传入的水印图片格式if (!waterRemarkPath.endsWith("png") && !waterRemarkPath.endsWith("PNG")) {throw new RuntimeException("向Excel上面打印水印,目前支持png格式的图片。");}// 加载图片ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();//图片的存放位置String path = "F:\\data\\erp\\" + waterRemarkPath;InputStream imageIn = new FileInputStream(path);//默认位置的图片// InputStream imageIn = Thread.currentThread().getContextClassLoader().getResourceAsStream(waterRemarkPath);if (null == imageIn || imageIn.available() < 1) {throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(1)。");}BufferedImage bufferImg = ImageIO.read(imageIn);if (null == bufferImg) {throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(2)。");}ImageIO.write(bufferImg, "png", byteArrayOut);// 开始打水印Drawing drawing = sheet.createDrawingPatriarch();// 按照共需打印多少行水印进行循环for (int yCount = 0; yCount < YCount; yCount++) {// 按照每行需要打印多少个水印进行循环for (int xCount = 0; xCount < XCount; xCount++) {// 创建水印图片位置int xIndexInteger = startXCol + (xCount * waterRemarkWidth) + (xCount * betweenXCol);int yIndexInteger = startYRow + (yCount * waterRemarkHeight) + (yCount * betweenYRow);/** 参数定义:第一个参数是(x轴的开始节点);第二个参数是(是y轴的开始节点);第三个参数是(是x轴的结束节点);* 第四个参数是(是y轴的结束节点);第五个参数是(是从Excel的第几列开始插入图片,从0开始计数);* 第六个参数是(是从excel的第几行开始插入图片,从0开始计数);第七个参数是(图片宽度,共多少列);* 第8个参数是(图片高度,共多少行);*/ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, xIndexInteger,yIndexInteger, xIndexInteger + waterRemarkWidth, yIndexInteger + waterRemarkHeight);Picture pic = drawing.createPicture(anchor, wb.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG));pic.resize();}}}

3.使用

  /*** 给Excel添加水印* @param wb 工作簿* @param sheet  sheet页* @param content 水印内容*/private static void painWaterMark(Workbook wb,Sheet sheet,String content) {String imgFileName = "waterMark_photo_"+content+".png";//创建水印图片  (默认保存到classes目录下)createWaterMark(content,imgFileName);//将图片写入到excel中try {//也可以动态获取sheet中的行和列,根据行和列适当的放置水印图片//获取excel实际所占行//int row = sheet.getFirstRowNum() + sheet.getLastRowNum();//获取excel实际所占列//int cell = sheet.getRow(sheet.getFirstRowNum()).getLastCellNum() + 1;putWaterRemarkToExcel(wb, sheet, imgFileName, 0, 0, 5, 5,2, 10, 2, 2);} catch (IOException e) {e.printStackTrace();}}

此时的导出的excel可能会支持修改,会造成水印图片支持拖动,所以可以对文件加密只读

 //excel加密只读
sheet.protectSheet(UUID.randomUUID().toString());

加密后的文件输入密码后支持修改

实现样式:

此部分代码只需要添加到excel写出之前(下图)就可以实现excel的添加水印

注意:1.此种方法添加的水印,如果在可修改状态会造成图片可拖动问题

2.图片大小是写死的没有根据水印文字进行自动变化大小

3.列是写死的,如果不知修改,可能会造成部分数据不能被查看

优化:

动态展示水印(水印看起来规整):

    /*** @param startXCol 水印起始列* @param startYRow 水印起始行* @param betweenXCol 水印横向之间间隔多少列* @param betweenYRow 水印纵向之间间隔多少行* @param XCount 横向共有水印多少个* @param YCount 纵向共有水印多少个* @param waterRemarkWidth 水印图片宽度为多少列* @param waterRemarkHeight 水印图片高度为多少行* @throws IOException*/// 也可以动态获取sheet中的行和列,根据行和列适当的放置水印图片// 获取excel实际所占行int row = sheet.getFirstRowNum() + sheet.getLastRowNum();// 获取excel实际所占列int cell = sheet.getRow(sheet.getFirstRowNum()).getLastCellNum() + 1;// 纵向个数,向上取整int YCount = row / (waterRemarkHeight + betweenYRow) + ((row % (waterRemarkHeight + betweenYRow) != 0) ? 1 : 0);// 横向个数,向上取整int XCount = cell / (waterRemarkWidth + betweenXCol) + ((cell % (waterRemarkWidth + betweenXCol) != 0) ? 1 : 0);

让列宽随着导出的列长自动适应:

autoSizeColumn(colNum)方法可能会报错,就需要跟踪所有的列,但是跟踪所有的列会使性能变差。
trackAllColumnsForAutoSizing()方法是SXSSFSheet的方法,如果使用的是Sheet类,就需要强转一下

 private void autoColumn() {//让列宽随着导出的列长自动适应int maxColumnWidth = 30 * 256;int columnNum = sheet.getRow(0).getPhysicalNumberOfCells();SXSSFSheet sheetAt;for (int colNum = 0; colNum < columnNum; colNum++) {//自动列宽sheetAt = (SXSSFSheet) sheet;// 跟踪所有用于自动调整大小的列,性能比较差sheetAt.trackAllColumnsForAutoSizing();sheet.autoSizeColumn(colNum);//like12 add,20220122,设置最大宽度限制int columnWidth = sheet.getColumnWidth(colNum);if (columnWidth > maxColumnWidth) {columnWidth = maxColumnWidth;}//手动调整列宽,解决中文不能自适应问题sheet.setColumnWidth(colNum, columnWidth * 12 / 10);}}

2.添加背景图片形式的水印

此方法是在excel中添加背景图片,所以不会影响操作修改

结合本地的项目,先进行导包

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

已经存在的包

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

1.还是先创建背景图片,使用的还是上面的方法

/*** 创建水印图片* 注意:生成的图片会默认保存到classes目录下,可以根据自己的业务进行更改** @param content* @param fileName* @return* @throws IOException*/private static String createWaterMark(String content, String fileName) {//生成水印图片的宽度Integer width = 700;//水印图片的高度Integer height = 300;// 获取bufferedImage对象BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);String fontType = "宋体";Integer fontStyle = java.awt.Font.PLAIN;Integer fontSize = 30;java.awt.Font font = new java.awt.Font(fontType, fontStyle, fontSize);// 获取Graphics2d对象Graphics2D g2d = image.createGraphics();image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);g2d.dispose();g2d = image.createGraphics();g2d.setColor(new java.awt.Color(0, 0, 0, 95)); //设置字体颜色和透明度// 设置字体g2d.setStroke(new BasicStroke(1));// 设置字体类型  加粗 大小g2d.setFont(font);//设置倾斜度g2d.rotate(Math.toRadians(-10), (double) image.getWidth() / 2, (double) image.getHeight() / 2);FontRenderContext context = g2d.getFontRenderContext();Rectangle2D bounds = font.getStringBounds(content, context);double x = (width - bounds.getWidth()) / 2;double y = (height - bounds.getHeight()) / 2;double ascent = -bounds.getY();double baseY = y + ascent;// 写入水印文字原定高度过小,所以累计写水印,增加高度g2d.drawString(content, (int) x, (int) baseY);// 设置透明度g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));// 释放对象g2d.dispose();//自定义存放位置String targetImagePath = "F:\\data\\erp\\" + fileName;//默认存放位置// String s = Thread.currentThread().getContextClassLoader().getResource("").getPath() + fileName;try {ImageIO.write(image, "png", new File(targetImagePath));} catch (IOException e) {e.printStackTrace();}return targetImagePath;}

2.添加背景水印

    /*** 添加水印背景图片** @param workbook 文件* @param content   水印*/
public static void setWaterMarkToExcel2(XSSFWorkbook workbook, String content) throws Exception {// 获取背景图片String imgFileName = "waterMark_photo" + StringUtils.trim(content) + ".png";String path = "F:\\data\\erp\\" + imgFileName;InputStream imageIn = new FileInputStream(path);//默认位置的图片// InputStream imageIn = Thread.currentThread().getContextClassLoader().getResourceAsStream(waterRemarkPath);if (imageIn.available() < 1) {throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(1)。");}BufferedImage bfi = ImageIO.read(imageIn);// 添加背景水印ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();ImageIO.write(bfi, "png", byteArrayOut);int pictureIdx = workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG);//add relation from sheet to the picture dataPOIXMLDocumentPart poixmlDocumentPart = workbook.getAllPictures().get(pictureIdx);for (int i = 0; i < workbook.getNumberOfSheets(); i++) {XSSFSheet xssfSheet = workbook.getSheetAt(i);PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();String relType = XSSFRelation.IMAGES.getRelation();PackageRelationship pr = xssfSheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);xssfSheet.getCTWorksheet().addNewPicture().setId(pr.getId());}}

3.整合的方法

    public void waterRemarkToExcel(XSSFWorkbook wb, String content) {String imgFileName = "waterMark_photo" + StringUtils.trim(content) + ".png";//创建水印图片createWaterMark(content, imgFileName);//添加背景水印try {setWaterMarkToExcel2(wb, content);} catch (Exception e) {e.printStackTrace();}}

4.写在工具类中 原来的工具类是使用的 Workbook类,所以先使用流转成XSSFWorkbook,然后写出。

工具类中本次使用的代码

       if (ArrayUtils.isNotEmpty(content) && content[0] != null) {//让列宽随着导出的列长自动适应// this.autoColumn();// 添加背景图片水印wb.write(bos);byte[] barray = bos.toByteArray();is = new ByteArrayInputStream(barray);XSSFWorkbook ww = new XSSFWorkbook(is);this.waterRemarkToExcel(ww, sheet, content[0]);out = new FileOutputStream(getAbsoluteFile(fullfolderName));ww.write(out);return Result.succeed();}

方法代码

    /*** 对list数据源将其里面的数据导入到excel表单** @param content 添加水印和加密不支持修改 (有值 是,并且是水印内容  没有值不添加,不加密)* @return 结果*/public Result exportExcel(String... content) {OutputStream out = null;ByteArrayOutputStream bos = new ByteArrayOutputStream();InputStream is = null;try {// 取出一共有多少个sheet.double sheetNo = Math.ceil(list.size() / sheetSize);for (int index = 0; index <= sheetNo; index++) {createSheet(sheetNo, index);// 产生一行Row row = sheet.createRow(0);int column = 0;// 写入各个字段的列头名称for (Object[] os : fields) {Excel excel = (Excel) os[1];this.createCell(excel, row, column++);}if (Type.EXPORT.equals(type)) {fillExcelData(index, row);addStatisticsRow();}}// 加水印的excel不支持修改,所以要保证列宽自动调整展示全部内容if (ArrayUtils.isNotEmpty(content) && content[0] != null) {//让列宽随着导出的列长自动适应// this.autoColumn();// 添加背景图片水印wb.write(bos);byte[] barray = bos.toByteArray();is = new ByteArrayInputStream(barray);XSSFWorkbook ww = new XSSFWorkbook(is);this.waterRemarkToExcel(ww, sheet, content[0]);out = new FileOutputStream(getAbsoluteFile(fullfolderName));ww.write(out);return Result.succeed();}out = new FileOutputStream(getAbsoluteFile(fullfolderName));wb.write(out);return Result.succeed();} catch (Exception e) {log.error("导出Excel异常{}", e.getMessage());throw new CustomException("导出Excel失败,请联系网站管理员!");} finally {if (wb != null) {try {wb.close();} catch (IOException e1) {e1.printStackTrace();}}if (out != null) {try {out.close();} catch (IOException e1) {e1.printStackTrace();}}if (is != null) {try {is.close();} catch (IOException e1) {e1.printStackTrace();}}try {bos.close();} catch (IOException e1) {e1.printStackTrace();}}}

展示

继续优化,图片不做单独处理,直接传输

涉及方法:

    /*** 创建水印图片** @param content 水印* @return* @throws IOException*/private BufferedImage createWaterMark(String content) {//生成水印图片的宽度Integer width = 700;//水印图片的高度Integer height = 300;// 获取bufferedImage对象BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);String fontType = "宋体";Integer fontStyle = java.awt.Font.PLAIN;Integer fontSize = 30;java.awt.Font font = new java.awt.Font(fontType, fontStyle, fontSize);// 获取Graphics2d对象Graphics2D g2d = image.createGraphics();image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);g2d.dispose();g2d = image.createGraphics();g2d.setColor(new java.awt.Color(0, 0, 0, 95)); //设置字体颜色和透明度// 设置字体g2d.setStroke(new BasicStroke(1));// 设置字体类型  加粗 大小g2d.setFont(font);//设置倾斜度g2d.rotate(Math.toRadians(-10), (double) image.getWidth() / 2, (double) image.getHeight() / 2);FontRenderContext context = g2d.getFontRenderContext();Rectangle2D bounds = font.getStringBounds(content, context);double x = (width - bounds.getWidth()) / 2;double y = (height - bounds.getHeight()) / 2;double ascent = -bounds.getY();double baseY = y + ascent;// 写入水印文字原定高度过小,所以累计写水印,增加高度g2d.drawString(content, (int) x, (int) baseY);// 设置透明度g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));// 释放对象g2d.dispose();return image;}

相关方法:

    /*** 创建水印图片,并添加进excel** @param content 水印* @param wb 文件*/private void waterRemarkToExcel(XSSFWorkbook wb, String content) throws Exception {//创建水印图片BufferedImage waterMark = createWaterMark(content);//添加背景水印this.setWaterMarkToExcel(wb, waterMark);}

生成的图片直接用于excel的背景图,不用在写入写出图片

遇到的问题:

问题描述:本地测试正常,部署到开发环境后,生成的背景图上的中文乱码

问题原因:经过查询,是因为linux上的中文是需要下载安装的,现在linux上并没有支持中文

指令查询:fc-list

支持的中文字体:fc-list :lang=zh

支持的英文字体:fc-list :lang=en

问题:excel有一键删除水印的功能

一键删除

所以想着锁定水印不支持修改。

经过资料查询没有找到先关的介绍,而且excel就不支持对水印的锁定。退而求其次,对部分不包含数据单元格加密(数据支持修改,支持复制)。在excel上进行操作,发现是可以达到效果的,现在使用java实现,代码如下:

 private void localRow(int rowCount) {CellStyle lockStyle = wb.createCellStyle();lockStyle.setLocked(true);CellStyle unlockStyle = wb.createCellStyle();unlockStyle.setLocked(false);//设置未锁定for (int i = 0; i < rowCount + 8; i++) {Row row = sheet.createRow(i);for (int j = 0; j < 4; j++) {Cell cell = row.createCell(j);cell.setCellStyle(unlockStyle);//默认是锁定状态;将所有单元格设置为:未锁定;然后再对需要上锁的单元格单独锁定//这里可以根据需要进行判断;我这就将第4列上锁了if (i > rowCount) {cell.setCellStyle(lockStyle);//将需要上锁的单元格进行锁定cell.setCellValue("上锁了");}}}//sheet添加保护,这个一定要否则光锁定还是可以编辑的sheet.protectSheet("123456");}

rowCount是填充数据行的最大值

int rowCount = sheet.getLastRowNum();

简单实现如图:

 上锁的区域不支持修改

融合进项目中代码: 对部分cell进行设置不加锁

                CellStyle unlockStyle = wb.createCellStyle();unlockStyle.setLocked(false);cell.setCellStyle(unlockStyle);// 加密sheet.protectSheet("123456");

注意:一个CellStyle可以进行多种set,一个cell只能进行一次setCellStyle。

效果:部分(没有数据的表格)加密

对背景的操作:

不可删除背景。

资料:Java实现导出Excel并附带水印

资料:Java语言为excel添加水印

资料:JAVA操作Excel表格部分不可编辑部分可编辑

导出的excel添加水印相关推荐

  1. Java - 导出Excel添加水印、密码

    需求: 导出excel时,添加操作人的用户名.手机号水印.文件密码.基于若依框架的excel导出工具类改造,有三种添加水印方式,此处仅做一个记录. 目录 一. 添加依赖 二.工具类 1.水印处理类 2 ...

  2. EasyExcell导出excel添加水印

    EasyExcell导出excel添加水印 1.添加easyExcel相关依赖 2.准备基础工具类 3.创建水印handler类 4.创建单元测试类WriteTest.class 5.测试结果 1.添 ...

  3. 数据导出到excel表格,并添加水印

    1.添加pom.xml依赖 <properties><java.version>1.8</java.version><hutool-poi.version&g ...

  4. Java为 pdf、word和excel添加水印

    1. 引入依赖 <!--easyexcel--><dependency><groupId>com.alibaba</groupId><artifa ...

  5. java导出为excel文件_java导出数据到excel文件

    有的时候,将一些有用的数据导出到excel是很有必要的.比如说,我现在在做一个学校的在线教学平台,有一个需求是:将学生成绩导出到excel文件中去. 那怎样实现用java导出数据到excel文件呢?? ...

  6. ftl模板导出excel_freemarker导出复杂Excel

    原文: freemarker导出复杂Excel date: 2017-04-20 12:39:04 [TOC] 序言 用Freemarker做Excel导出确实很容易. 但是导出复杂Excel, 例如 ...

  7. Gridview导出到Excel,Gridview中的各类控件,Gridview中删除记录的处理

    Asp.net 2.0中新增的gridview控件,是十分强大的数据展示控件,在前面的系列文章里,分别展示了其中很多的基本用法和技巧(详见< ASP.NET 2.0中Gridview控件高级技巧 ...

  8. 将数据库查询结果导出成Excel表格

    使用Java代码,从数据库中获取结果集,将结果集导出成Excel表格形式. 从数据库中查询学生表所有数据,将其导出成Excel表格,点击查看学生表表结构 . package com.test.test ...

  9. 百度指数常见php框架,怎么导出数据到excel表格-如何将百度指数数据导出到Excel表格...

    如何将百度指数数据导出到Excel表格 第一步:打开CAD.CAD命令行输入"Li"."选择对象"选需要提取坐标的多段线.回车. 第二步:将CAD文本框中的数据 ...

  10. mysql excel 命令行_MySQL 命令行数据导出到 Excel

    显示行号 | 选择喜欢的代码风格 默认 GitHub Dune LakeSide Plateau Vibrant Blue Eighties Tranquil 将 MySQL 的数据导出 Excel, ...

最新文章

  1. android cookie保存,android本地保存Cookie和还原到cookiestore
  2. 设置gbk_我的gVim设置
  3. java readline 实现_基于自定义BufferedReader中的read和readLine方法
  4. P5496-[模板]回文自动机【PAM】
  5. 实战CGLib系列之proxy:方法拦截MethodInterceptor
  6. 编译器入门 语法分析器 java_从零开始写个编译器吧 - Parser 语法分析器
  7. 从 Dagger 到 Hilt,谷歌为何执着于让我们用依赖注入?
  8. 【BZOJ3576】江南乐,博弈
  9. C语言学习笔记---结构体的定义和初始化
  10. html 预加载图片,实现网页图片预加载的几个方法
  11. Android基站定位源代码
  12. 数据结构与算法之排序
  13. ArcGIS空间数据分析实用工具——方向分布(标准差椭圆)
  14. html5写自我介绍,一分钟标签式自我介绍4篇
  15. 一个屌丝程序猿的人生(八十三)
  16. 一篇文章让你了解大数据挖掘技术
  17. Python | 判定IP地址合法性的四种方法
  18. Jetson Nano Nano 2G 官方镜像下载地址
  19. Android设置屏幕亮度
  20. 2019计算机网络原理04741,04741计算机网络原理201910.doc

热门文章

  1. 手把手教你安装Xposed框架+JustTrustMe抓取手机APP数据
  2. Hadoop系列五之版本差别
  3. 树莓派小车C语言循迹,自动循迹小车_单片机/STM32/树莓派/Arduino/开发板创意项目-聚丰项目-电子发烧友网...
  4. 抖音视频如何一键批量下载
  5. centos php ioncube_CentOS 7安装ionCube Loader为php解密组件的方法
  6. pyecharts中文手册
  7. ipv6overipv4+linux,IPv4 over IPv6 的配置
  8. linux共用home分区,安装UOS和Deepin双系统并且共用/home分区的测试报告
  9. LU分解_SVD分解
  10. 【Python-tkinter】拼音输入方法——小学拼音练习题