Aspose导出word使用记录
背景:Aspose系列的控件,功能实现都比较强大,可以实现多样化的报表设计及输出。
通过这次业务机会,锂宝碳审核中业务功需要实现Word文档表格的动态导出功能,因此学习了相关内容,在学习和参考了官方API文档的帮助下,将学习和简单的使用记录在wiki中。下面由我来简单介绍这个控件在实际业务中的使用过程。
一、开发前准备
1. 问题产生: 在实际的开发过程中会遇到Aspose依赖无法导入的情况,如下图所示
2. 原因分析: 主要原因是因为Maven的pom配置中使用的是阿里的镜像源,而阿里云镜像无法下载aspose依赖
3. 解决方法:
(1)先将 aspose的jar包 下载到本地,然后添加到本地的maven,再上传到公司的仓库(文档选择了第一种方式)
(2)提供aspose的仓库地址,将仓库地址加入nexusjar包下载地址
4. 方法步骤:
(1)在配置好后创建项目后,在配置文件需要引用依赖,可以选择本地依赖,而本地依赖需要导入, 所以就需要进行jar包的导入
(2)导入包的语句:
mvn install:install-file "-Dfile=D:\software\Jar\aspose-words-15.12.0-jdk16.jar" "-DgroupId=com.aspose" "-DartifactId=aspose-words" "-Dversion=15.12.0" "-Dpackaging=jar"
(3)解释一下这些参数意思:
-Dfile: jar包所在本地的绝对路径
-DgroupId: 在导入包后会在本地仓库创建一个com.aspose的路径,包就会存放在jar里面
-DartifactId=spring: jar包的名字
-Dversion: jar包的版本
-Dpackaging: 导入包的类型
(4)进入cmd运行上面的命令:显示如下即加入成功!这就是Maven的下载和在IDEA的配置以及jar包的导入
二、word表格的基本操作
1. 建立word模板
(1)在word文档中,在菜单栏中依次点击 插入
-> 域
(2)接着在弹出框中选择 邮件合并
域,在域属性中填写域名,该域名即为变量名,填写完毕后点击 确定
即可
(3)生成如图所示的样式,即为添加成功
2. 定义集合变量
当单一变量无法满足需求后,我们就需要定义集合变量,我就拿员工薪资统计来举例,一个表格里面会有多个数据。此时,我们应该插入
List
数据到模板文件中
(1)模板关键字:TableStart:集合名称
, TableEnd:集合名称
(2)同样的,在域中选择邮件合并
,域名要用TableStart
打头,紧接冒号:,再然后就是定义集合的名称。需要注意的是,TableStart
意为集合的开始,所以我们再集合结束的地方,需要添加结束标识TableEnd
(3)TableStart
和 TableEnd
之间的变量,就是集合中每个属性的变量名称
(4)添加完毕之后,如图所示,模板 也可点击这里下载(5vy5)
3. 员工薪资数据插入Demo案例
3.1 导入工具类
package org.example;import com.aspose.words.*;
import com.aspose.words.net.System.Data.DataRow;
import com.aspose.words.net.System.Data.DataTable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author fangxiang* @date 2023/7/7 13:46*/
public class ContractUtil {private ContractUtil() {}/*** 调整图片的大小格式属性** @param source BufferedImage 原始image* @param targetW int 目标宽* @param targetH int 目标高* @param flag boolean 是否同比例调整* @return BufferedImage 返回新image*/public static BufferedImage resizeBufferedImage(BufferedImage source, int targetW, int targetH, boolean flag) {int type = source.getType();BufferedImage target = null;// 计算缩放比例double sx = (double) targetW / source.getWidth();double sy = (double) targetH / source.getHeight();// 根据保持纵横比的标志调整缩放比例if (flag && sx > sy) {sx = sy;targetW = (int) (sx * source.getWidth());} else if (flag && sx <= sy) {sy = sx;targetH = (int) (sy * source.getHeight());}// 创建目标BufferedImageif (type == BufferedImage.TYPE_CUSTOM) {ColorModel cm = source.getColorModel();WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);boolean alphaPremultiplied = cm.isAlphaPremultiplied();target = new BufferedImage(cm, raster, alphaPremultiplied, null);} else {target = new BufferedImage(targetW, targetH, type);}// 绘制调整大小后的图像Graphics2D g = target.createGraphics();g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));// 释放资源g.dispose();return target;}/*** 使用对象数据填充Word模板** @param modelWordByte Word模板的二进制数据* @param obj 要填充的数据对象* @return 填充数据后的Word二进制数据*/public static byte[] fillWordDataByDomain(byte[] modelWordByte, Object obj) {try {// 获取对象的ClassClass<?> aClass = obj.getClass();// 获取对象的所有字段Field[] fields = aClass.getDeclaredFields();// 创建一个Map来存储字段名和字段值Map<String, Object> data = new HashMap<>(fields.length);// 遍历字段,获取字段值并存入Map中for (Field field : fields) {// 获取字段的属性描述器PropertyDescriptor pd = new PropertyDescriptor(field.getName(), aClass);// 获取字段的读取方法Method method = pd.getReadMethod();// 获取字段名String key = field.getName();// 调用读取方法获取字段值Object value = method.invoke(obj);// 如果字段值不为空,则将字段名和字段值存入Map中if (value != null) {data.put(key, value);}}// 使用Map数据填充Word模板并返回填充后的Word二进制数据return fillWordDataByBinary(modelWordByte, data);} catch (Exception e) {// 出现异常时返回空的字节数组return new byte[0];}}/*** 填充 word 模板(map数据格式)** @param file word二进制* @param data 要填充的数据* @return 组合数据之后的word二进制*/public static byte[] fillWordDataByBinary(byte[] file, Map<String, Object> data) throws Exception {byte[] ret = null;if (CollectionUtils.isEmpty(data)) {return ret;}try (InputStream is = new ByteArrayInputStream(file);ByteArrayOutputStream out = new ByteArrayOutputStream()) {// 读取Word文档Document doc = new Document(is);// 用于存储需要替换的字段和对应的值Map<String, String> toData = new HashMap<>();// 遍历数据Mapfor (Map.Entry<String, Object> en : data.entrySet()) {String key = en.getKey();Object value = en.getValue();// 如果值是List类型,则填充表格数据if (value instanceof List) {//写入表数据DataTable dataTable = fillListData((List) value, key);doc.getMailMerge().executeWithRegions(dataTable);}String valueStr = String.valueOf(en.getValue());// 如果值为空或者为字符串"null",则跳过该字段if (StringUtils.isEmpty(value)) {continue;}// 将字段和值存入toDatatoData.put(key, valueStr);}// 将toData中的字段和值分别存入两个数组String[] fieldNames = new String[toData.size()];String[] values = new String[toData.size()];int i = 0;for (Map.Entry<String, String> entry : toData.entrySet()) {fieldNames[i] = entry.getKey();values[i] = entry.getValue();i++;}// 执行合并数据操作doc.getMailMerge().execute(fieldNames, values);// 保存修改后的文档到输出流doc.save(out, SaveOptions.createSaveOptions(SaveFormat.DOCX));ret = out.toByteArray();}return ret;}/*** 封装 list 数据到 word 模板中(word表格)** @param list 数据* @param tableName 表格列表变量名称* @return word表格数据DataTable*/private static DataTable fillListData(List<Object> list, String tableName) throws IntrospectionException, InvocationTargetException, IllegalAccessException {//创建DataTable,并绑定字段DataTable dataTable = new DataTable(tableName);// 遍历 list 中的每个对象for (Object obj : list) {// 创建DataRow,封装该行数据DataRow dataRow = dataTable.newRow();Class<?> objClass = obj.getClass();Field[] fields = objClass.getDeclaredFields();// 遍历对象的每个字段for (int i = 0; i < fields.length; i++) {Field field = fields[i];// 将字段名添加到 dataTable 的列中dataTable.getColumns().add(fields[i].getName());// 通过反射获取字段的值,并将其设置到 dataRow 中PropertyDescriptor pd = new PropertyDescriptor(field.getName(), objClass);Method method = pd.getReadMethod();dataRow.set(i, method.invoke(obj));}// 将 dataRow 添加到 dataTable 的行中dataTable.getRows().add(dataRow);}return dataTable;}/*** 利用反射将字段转为Map键值对** @param obj* @return* @throws IntrospectionException* @throws IllegalAccessException* @throws InvocationTargetException*/public static Map<String, Object> getStringObjectMap(Object obj) throws IntrospectionException, IllegalAccessException, InvocationTargetException {Class<?> aClass = obj.getClass();Field[] fields = aClass.getDeclaredFields();Map<String, Object> data = new HashMap<>(fields.length);// 遍历对象的所有属性for (Field field : fields) {// 获取属性的名称String fieldName = field.getName();// 创建属性描述器,用于获取属性的读取方法PropertyDescriptor pd = new PropertyDescriptor(fieldName, aClass);// 获取属性的读取方法Method method = pd.getReadMethod();// 获取属性的值Object value = method.invoke(obj);// 如果属性的值不为空,则将属性名和属性值添加到Map中if (value != null) {data.put(fieldName, value);}}return data;}/*** 通过Map的方式填充word模板** @param templatePath* @param targetPath* @param data* @param fileType*/public static void fillWordDataByMap(String templatePath, String targetPath, Map<String, Object> data, int fileType) {try (InputStream is = new FileInputStream(templatePath);OutputStream out = new FileOutputStream(targetPath)) {// 加载模板文件Document doc = new Document(is);DocumentBuilder builder = new DocumentBuilder(doc);Map<String, String> toData = new HashMap<>();// 遍历数据,填充模板for (Map.Entry<String, Object> en : data.entrySet()) {String key = en.getKey();Object value = en.getValue();if (key == null || value == null) {continue;}if (value instanceof List) {// 填充表格数据DataTable dataTable = fillListData((List) value, key);doc.getMailMerge().executeWithRegions(dataTable);}if (value instanceof BufferedImage) {// 填充图像数据builder.moveToMergeField(key);builder.insertImage((BufferedImage) value);}String valueStr = String.valueOf(en.getValue());toData.put(key, valueStr);}// 准备合并数据的数组String[] fieldNames = new String[toData.size()];String[] values = new String[toData.size()];int i = 0;for (Map.Entry<String, String> entry : toData.entrySet()) {fieldNames[i] = entry.getKey();values[i] = entry.getValue();i++;}// 合并数据到模板doc.getMailMerge().execute(fieldNames, values);// 保存目标文件doc.save(out, SaveOptions.createSaveOptions(fileType));} catch (Exception e) {throw new RuntimeException(e);}}// private static License license = null;// /**
// * 加载 license
// * 由于 aspose是商业组件,若没有license(可自行在网上查询license.xml),则会出现水印。
// */
// static {
// try {
// InputStream is = ContractUtil.class.getResourceAsStream("/license.xml");
// license = new License();
// license.setLicense(is);
// } catch (Exception e) {
// throw new RuntimeException("自动加载aspose证书文件失败!");
// }
// }}
3.2 创建实体Bean
(1)首先,我们先定义和模板对应的数据,我们先定义列表里面的 员工薪资信息中 Worker
的数据
package VO;import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.math.BigDecimal;
import java.util.List;/*** 员工薪资信息** @author fangxiang* @date 2023/7/7 13:47*/
@Data
@Builder
@AllArgsConstructor
public class Worker {/*** 员工姓名*/private String name;/*** 年龄*/private Integer age;/*** 性别*/private String sex;/*** 工资*/private BigDecimal salary;
}
(2)接着我们定义整个模板的基本数据:SalaryData
package VO;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.awt.image.BufferedImage;
import java.math.BigDecimal;
import java.util.List;/*** 文档模板数据** @author fangxiang* @date 2023/7/7 13:46*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SalaryData {/*** 公司名称*/private String companyName;/*** 员工薪资列表*/private List<Worker> workerList;/*** 工资总和*/private BigDecimal totalSalary;/*** 公司log*/private BufferedImage logImg;
}
(3)定义好以上数据之后,我们就可以开始进行数据的填充,工具类提供两种填充方法(代码案例是在本地执行的,所以记得替换本地的图片和文件地址)
1.一种是传递 Object 数据进行数据填充
2.另一种是传递 Map 数据进行数据填充
这里采用第一种方式进行数据的填充,个人也比较推荐使用domain数据
package org.example;import VO.SalaryData;
import VO.Worker;
import com.aspose.words.SaveFormat;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.beans.IntrospectionException;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;/**** @author fangxiang* @date 2023/7/7 13:47* */
public class Test {public static void main(String[] args) throws IOException, IntrospectionException, InvocationTargetException, IllegalAccessException {// 封装员工薪资信息Worker worker1 = Worker.builder().name("李雄").age(25).sex("男").salary(BigDecimal.valueOf(1000)).build();Worker worker2 = Worker.builder().name("李浩").age(25).sex("男").salary(BigDecimal.valueOf(2000)).build();List<Worker> workerList = Arrays.asList(worker1, worker2);// 计算工资总和BigDecimal totalSalary = workerList.stream().map(Worker::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add);// 读取图片BufferedImage image = ImageIO.read(new FileInputStream("C:\\Users\\fangxiang\\Desktop\\鱼.png"));BufferedImage bufferedImage = ContractUtil.resizeBufferedImage(image, 100, 100, true);// 封装文档模板数据SalaryData data = SalaryData.builder().companyName("上班不摸鱼有限公司").workerList(workerList).totalSalary(totalSalary).logImg(bufferedImage).build();// 读取模板文件Map<String, Object> dataMap = ContractUtil.getStringObjectMap(data);String templatePath2 = "C:\\Users\\fangxiang\\Desktop\\模板.docx";String docxPath = "C:\\Users\\fangxiang\\Desktop\\员工薪资统计表.docx";ContractUtil.fillWordDataByMap(templatePath2, docxPath, dataMap, SaveFormat.DOCX);}
}
3.3 结果展示
(1)水印说明
看上方的图相比大家也不难发现,文档生成的同时,也多了红色的一串字体。
由于aspose
是商业组件的,所以想要去水印使用,需要添加 license.xml
,由于实际业务中使用的是公司提供的凭证,所以我就不传上来了,如有需要,可以选择购买 aspose
的服务,或者上网搜一个 license.xml
(2)如何添加 license
我已经在工具类中提供了一个静态初始化方法,大家下载好 license.xml 之后,放入对应的目录,将注释部分去掉即可
相关文章:

Aspose导出word使用记录
背景:Aspose系列的控件,功能实现都比较强大,可以实现多样化的报表设计及输出。 通过这次业务机会,锂宝碳审核中业务功需要实现Word文档表格的动态导出功能,因此学习了相关内容,在学习和参考了官方API文档的…...
[Java]_[初级]_[使用SAX流的方式写入XML文件]
场景 文件的写入目前没有发现可以增量写入的,只能是完全重新写入。对于大量数据需要写入XML文件,还是和读XML文件一样,不需要生成DOM模型能节省不少的内存和指令。 说明 在java标准库里,也是有相关的SAX类来写入数据流…...
java里面封装https请求工具类
1.工具类如下 Component Slf4j public class RestClientUtil<T> {private final RestTemplate restTemplate;public RestClientUtil() {this.restTemplate new RestTemplate();}public JSONObject uploadFile(String url, String fileUrl) throws IOException {List<…...
uniApp常见面试题-附详细答案
uniApp中如何进行页面跳转? 答案:可以使用uni.navigateTo、uni.redirectTo和uni.reLaunch等方法进行页面跳转。其中,uni.navigateTo可以实现页面的普通跳转,uni.redirectTo可以实现页面的重定向跳转,uni.reLaunch可以实…...

Java“牵手”1688整店商品API接口数据,通过店铺ID获取整店商品详情数据,1688店铺所有商品API申请指南
1688平台店铺所有商品数据接口是开放平台提供的一种API接口,通过调用API接口,开发者可以获取1688整店的商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片、价格信息等详细信息 。 获取店铺所有商品接口API是一种用于获取电商平台上商品详…...

数据进制的转换
其他进制转换为十进制 通过按权展开法转换 十进制转换为其他进制 通过短除法转换(注意计算结果是倒着的) 例如将十进制的94转换为二进制 二进制转八进制和十六进制 3位二进制数表示1位八进制数,4位二进制数表示1位十六进制数 同理八进制数…...
如何分析识别文章/内容中高频词和关键词?
theme: orange 要分析一篇文章的高频词和关键词,可以使用 Python 中的 nltk 库和 collections 库或者jieba库来实现,本篇文章介绍基于两种库分别实现分析内容中的高频词和关键词。 nltk 和 collections 库 首先,需要安装 nltk 库和 collectio…...

Windows7安装SSH客户端的解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...
力扣:81. 搜索旋转排序数组 II(Python3)
题目: 已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k1], .…...
TCP IP网络编程(三) 地址族与数据序列
文章目录 分配给套接字的IP地址与端口号网络地址网络地址分类与主机地址边界 地址信息的表示表示 IPv4地址的结构体结构体sockaddr_in 的成员分析 网络字节序与地址变换字节序与网络字节序字节序转换 网络地址的初始化与分配将字符串信息转换为网络字节序的整数型网络地址初始化…...

对比Flink、Storm、Spark Streaming 的反压机制
分析&回答 Flink 反压机制 Flink 如何处理反压? Storm 反压机制 Storm反压机制 Storm 在每一个 Bolt 都会有一个监测反压的线程(Backpressure Thread),这个线程一但检测到 Bolt 里的接收队列(recv queue)出现了…...
Ubuntu常用配置集合
Ubuntu配置软件镜像源 参考文章:Ubuntu如何配置软件镜像源 建议使用清华的源。 Ubuntu安装SSH服务: 参考文章:Ubuntu安装SSH服务 ubuntu下安装使用nvm 参考文章:ubuntu下安装使用nvm 出现下载sh文件不成功的情况,…...
传统三维重建和深度学习三维重建 MVS笔记总结、问题总结
什么是cost-volume ?(代价体) 什么是置信度?置信区间? pixel-wise,patch-wise,image-wise的区别 图像 4领域-8领域-16领域 及代码实现 文章目录 1 plane-sweeping2 传统三维重建深度学习三维重建有何不同呢?3 大型场景重建4 PMVS-精确、密集、鲁棒的多视图立体视觉…...

Ansible学习笔记10
1、在group1的被管理机里的mariadb里创建一个abc库; 1) 然后我们到agent主机上进行检查: 可以看到数据库已经创建成功。 再看几个其他命令: #a组主机重启mysql,并设置开机自启 ansible a -m service -a "namemy…...

肖sir__linux详解__002(系统命令)
linux系统命令 1、df 查看磁盘使用情况 (1)df 查看磁盘使用情况(按kb单位显示) (2)df -h 按单位显示磁盘使用情况 2、top 实时查看动态进程 (1)top 详解: 第一行&…...

AI绘画:StableDiffusion实操教程-斗罗大陆2-江楠楠-常服(附高清图下载)
前段时间我分享了StableDiffusion的非常完整的教程:“AI绘画:Stable Diffusion 终极宝典:从入门到精通 ” 尽管如此,还有读者反馈说,尽管已经成功安装,但生成的图片与我展示的结果相去甚远。真实感和质感之…...

JavaScript运行机制与实践应用
一、JavsScript运行机制 1、JavaScript 是一种解释型语言,它的执行机制主要包括以下几个步骤: 2、事件循环 3、JavaScript运行模型 4、JavaScript任务 5、JavaScript宏任务和微任务 6、案例分析 console.log(script start) setTimeout(function () {co…...

【算法奥义】最大矩形问题
首先建立一个二维数组,这个二维数组,计算出矩阵的每个元素的左边连续 1 的数量,使用二维数组 left记录,其中left[i][j] 为矩阵第 i 行第 j 列元素的左边连续 1 的数量。 也就是从这个元素开始,从右往左边数有多少个连…...
06 Kafka线上集群部署方案
kafka部署在linux上有什么好处 网络传输效率 kafka部署在linux上,可以用到linux的零拷贝提升网络传输效率,提高kafka的吞吐量。利用零拷贝可以使数据不经过用户态直接通过网卡发送给接收方,实现数据的高性能传输 kafka和零拷贝技术 kafka…...
flex-shrink计算题
当我们使用 flexbox 布局时,flex-shrink 属性用于指定 flex 项在空间不足时收缩的比例。它表示了一个 flex 项相对于其他 flex 项收缩的比例。 假设有一个 flex 容器,其中包含三个子项,它们的 flex-shrink 分别设置为 1、2 和 3。当容器的可…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...