当前位置: 首页 > news >正文

Java Email 发HTML邮件工具 采用 freemarker模板引擎渲染

Java Email 发HTML邮件工具 采用 freemarker模板引擎

1.常用方式对比

Java发送邮件有很多的实现方式

  • 第一种:Java 原生发邮件mail.jaractivation.jar

    <!-- https://mvnrepository.com/artifact/javax.mail/mail -->
    <dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.activation/activation -->
    <dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version>
    </dependency>
    

    原生的java可以发送普通文本和HTML邮件,同时也可以发送带附件的邮件,但是缺点也很明显,配置非常繁琐,不同的邮件需要不同的实现类去完成,不适合项目中使用。

  • 第二种:使用框架提供的去实现,在SpringBoot中有实现这个功能的组件spring-boot-starter-mail

    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail -->
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    

    这种实现起来特别方便,仅仅只要在SpringBoot项目中引入组件,在配置文件中配置好各种参数,就可以实现依赖注入,调用接口完成发送邮件,同时支持普通文本、HTML邮件、以及携带附件的邮件。同时缺点就是和框架集成太高了,如果项目中没有使用SpringBoot就不是那么好用了。

  • 第三种:采用Apache提供的邮件工具commons-email,项目中已经封装好了一些常用的发邮件的接口供开发者使用,并且配置起来也比较简单,和其他技术耦合低是比较好的解决问题的方案。

    <dependency><groupId>org.apache.commons</groupId><artifactId>commons-email</artifactId><version>1.5</version>
    </dependency>
    

2.项目中使用

项目使用的jar包,采用maven工程。

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-email</artifactId><version>1.5</version>
</dependency>

第三方jar包引入时下载:https://mvnrepository.com/artifact/org.apache.commons/commons-email

(1)创建配置文件email.properties

  • 建议将配置文件统一放置在resources文件夹中
# 邮件服务器的地址
host=smtp.exmail.qq.com
# 邮件服务器的端口(百度上有,或者邮件提供商开发文档有)
port=456
# 发件人邮箱
username=
# 密码或授权码
password=
# 发件人名称,如果不提供会使用发件人邮件作为发件人
formName=

(2)发邮件的核心代码

package com.zhongcode.demo.email;import org.apache.commons.mail.*;import java.util.Collections;
import java.util.List;
import java.util.Properties;/*** 邮件发送实例*/
public class SendEmail {/*** 邮件服务器 host*/private String host;/*** 邮件服务器端口 port*/private int port;/*** 用户名*/private String username;/*** 密码(授权码)*/private String password;/*** 发件人名称*/private String formName;/*** 是否开启debug*/private boolean debug;public SendEmail() {}public SendEmail(Properties pro) {this.host = pro.getProperty("host");this.port = Integer.parseInt(pro.getProperty("port", "0"));this.username = pro.getProperty("username");this.password = pro.getProperty("password");this.formName = pro.getProperty("formName");}public SendEmail(String host, int port, String username, String password, String formName, boolean debug) {this.host = host;this.port = port;this.username = username;this.password = password;this.formName = formName;this.debug = debug;}public SendEmail(String host, int port, String username, String password, String formName) {this.host = host;this.port = port;this.username = username;this.password = password;this.formName = formName;}public SendEmail(String host, int port, String username, String password) {this.host = host;this.port = port;this.username = username;this.password = password;}public String getHost() {return host;}public void setHost(String host) {this.host = host;}public int getPort() {return port;}public void setPort(int port) {this.port = port;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getFormName() {return formName;}public void setFormName(String formName) {this.formName = formName;}public boolean isDebug() {return debug;}public void setDebug(boolean debug) {this.debug = debug;}/*** 发邮件** @param to      收件件人* @param subject 主题* @param msg     消息* @throws EmailException 邮件异常*/public synchronized void sendEmail(String to, String subject, String msg) throws EmailException {sendEmail(Collections.singletonList(to), subject, msg, null, null);}/*** 发邮件** @param toEmails 发送列表* @param subject  主题* @param msg      内容支持html* @param attaches 附件* @param fromName 发送名称* @throws EmailException 邮件异常*/public synchronized void sendEmail(List<String> toEmails, String subject, String msg, List<String> attaches, String fromName) throws EmailException {// 创建邮件对象MultiPartEmail email;// 当附件不为空时,使用MultiPartEmail添加附件if (attaches != null) {email = new MultiPartEmail();for (String att : attaches) {EmailAttachment attachment = new EmailAttachment();attachment.setPath(att);// 截取文件名attachment.setName(att.substring(att.lastIndexOf("/") + 1));email.attach(attachment);}// 当使用MultiPartEmail时渲染html要使用Part进行添加email.addPart(msg, "text/html; charset=UTF-8");} else {// 默认使用HtmlEmail创建email = new HtmlEmail();email.setMsg(msg);}// 设置主机email.setHostName(host);// 设置端口email.setSmtpPort(port);email.setDebug(debug);email.setAuthenticator(new DefaultAuthenticator(username, password));email.setCharset("UTF-8");email.setSSLOnConnect(true);email.setFrom(username, fromName == null ? (this.formName == null ? username : this.formName) : fromName);email.setSubject(subject);for (String to : toEmails) {email.addTo(to);}email.send();}
}

(3)程序发邮件测试

public static void main(String[] args) throws EmailException, IOException, TemplateException {// 加载配置文件InputStream is = Main.class.getClassLoader().getResourceAsStream("email.properties");Properties pro = new Properties();pro.load(is);// 创建邮件SendEmail sendEmail = new SendEmail(pro);sendEmail.sendEmail("收件人", "test", "测试邮件");
}

3.发送HTML模板邮件

当我们想发送模板HTML时,需要发送HTML格式的代码,主要是纯文本的邮件并不好看,而且项目中经常使用的是好看的模板,比如系统用户注册时需要发送验证码,很多情况下都要发邮件。

这里我们采用的freemarker模板引擎来渲染我们的HTML模板,下面是以发送邮箱验证码为例。

(1)引入freemarker模板引擎

<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.32</version>
</dependency>

(2)创建HTML模板

文件名:code.ftl

<meta charset="utf-8"><table width="100%"><tr><td style="width: 100%;"><center><table class="content-wrap" style="margin: 0px auto; width: 600px;"><tr><td style="margin: 0px auto; overflow: hidden; padding: 0px; border: 0px dotted rgb(238, 238, 238);"><!----><div class="full" tindex="1" style="margin: 0px auto; max-width: 600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" class="fullTable" style="width: 600px;"><tbody><tr><td class="fullTd" style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-color: rgb(170, 0, 170); background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;"><tr><td align="left" style="font-size: 0px; padding: 20px;"><div class="text" style="font-family: 微软雅黑, &quot;Microsoft YaHei&quot;; overflow-wrap: break-word; margin: 0px; text-align: center; line-height: 1.6; color: rgb(255, 255, 255); font-size: 18px; font-weight: normal;"><div><p style="text-size-adjust: none; word-break: break-word; line-height: 1.6; font-size: 18px; margin: 0px;"><strong>系统提示</strong></p></div></div></td></tr></table></td></tr></tbody></table></div><div class="full" tindex="2" style="margin: 0px auto; max-width: 600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" class="fullTable" style="width: 600px;"><tbody><tr><td class="fullTd" style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;"><tr><td align="left" style="font-size: 0px; padding: 20px 20px 5px;"><div class="text" style="font-family: 微软雅黑, &quot;Microsoft YaHei&quot;; overflow-wrap: break-word; margin: 0px; text-align: left; line-height: 20px; color: rgb(102, 102, 102); font-size: 14px; font-weight: normal;"><div><p style="text-size-adjust: none; word-break: break-word; line-height: 20px; font-size: 14px; margin: 0px;">您的验证码为:</p></div></div></td></tr></table></td></tr></tbody></table></div><div class="full" tindex="3" style="margin: 0px auto; max-width: 600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" class="fullTable" style="width: 600px;"><tbody><tr><td class="fullTd" style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;"><tr><td align="left" style="font-size: 0px; padding: 20px;"><div class="text" style="font-family: 微软雅黑, &quot;Microsoft YaHei&quot;; overflow-wrap: break-word; margin: 0px; text-align: center; line-height: 20px; color: rgb(0, 0, 0); font-size: 24px; font-weight: normal;"><div><p style="text-size-adjust: none; word-break: break-word; line-height: 20px; font-size: 24px; margin: 0px;"><strong>${code}</strong></p></div></div></td></tr></table></td></tr></tbody></table></div><div class="full" tindex="4" style="margin: 0px auto; max-width: 600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" class="fullTable" style="width: 600px;"><tbody><tr><td class="fullTd" style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;"><tr><td align="left" style="font-size: 0px; padding: 8px 20px 20px;"><div class="text" style="font-family: 微软雅黑, &quot;Microsoft YaHei&quot;; overflow-wrap: break-word; margin: 0px; text-align: left; line-height: 20px; color: rgb(102, 102, 102); font-size: 12px; font-weight: normal;"><div><p style="text-size-adjust: none; word-break: break-word; line-height: 20px; font-size: 12px; margin: 0px;"><span style="color: #333333;">验证码10分钟内有效。</span></p></div></div></td></tr></table></td></tr></tbody></table></div><div class="full" tindex="5" style="margin: 0px auto; line-height: 0px; max-width: 600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" class="fullTable" style="width: 600px;"><tbody><tr><td align="center" class="fullTd" style="direction: ltr; font-size: 0px; padding: 10px 20px; text-align: center; vertical-align: top; word-break: break-word; width: 600px; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;"><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px;"><tbody><tr><td style="width: 600px; border-top: 1px solid rgb(204, 204, 204);"></td></tr></tbody></table></td></tr></tbody></table></div><div class="full" tindex="6" style="margin: 0px auto; max-width: 600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" class="fullTable" style="width: 600px;"><tbody><tr><td class="fullTd" style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;"><tr><td align="left" style="font-size: 0px; padding: 10px 20px 0px;"><div class="text" style="font-family: 微软雅黑, &quot;Microsoft YaHei&quot;; overflow-wrap: break-word; margin: 0px; text-align: left; line-height: 20px; color: rgb(170, 170, 170); font-size: 12px; font-weight: normal;"><div><p style="text-size-adjust: none; word-break: break-word; line-height: 20px; font-size: 12px; margin: 0px;">此为系统邮件请勿回复</p></div></div></td></tr></table></td></tr></tbody></table></div><div class="full" tindex="7" style="margin: 0px auto; max-width: 600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" class="fullTable" style="width: 600px;"><tbody><tr><td class="fullTd" style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;"><table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;"><tr><td align="left" style="font-size: 0px; padding: 20px;"><div class="text" style="font-family: 微软雅黑, &quot;Microsoft YaHei&quot;; overflow-wrap: break-word; margin: 0px; text-align: center; line-height: 20px; color: rgb(170, 170, 170); font-size: 12px; font-weight: normal;"><div><p style="text-size-adjust: none; word-break: break-word; line-height: 20px; font-size: 12px; margin: 0px;">Copyright © zhongcode 2020 All Right Reserved</p></div></div></td></tr></table></td></tr></tbody></table></div></td></tr></table></center></td></tr></table>

(3)代码测试

public class Main {public static void main(String[] args) throws EmailException, IOException, TemplateException {//1.创建配置类Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);//2.设置模板所在的目录configuration.setDirectoryForTemplateLoading(new File("E:\\tmp\\ftl"));//3.设置字符集configuration.setDefaultEncoding("utf-8");//4.加载模板//模板名自动在模板所在目录寻找Template template = configuration.getTemplate("code.ftl");Map<String, String> data = new HashMap<>();data.put("code", "999999");StringWriter out = new StringWriter();template.process(data, out);// 加载配置文件InputStream is = Main.class.getClassLoader().getResourceAsStream("email.properties");Properties pro = new Properties();pro.load(is);// 创建邮件SendEmail sendEmail = new SendEmail(pro);sendEmail.sendEmail("收件人", "验证码", out.toString());}}

在这里插入图片描述

就这样,一封好看的HTML邮件就完成了,任何问题都可以私信我哦

相关文章:

Java Email 发HTML邮件工具 采用 freemarker模板引擎渲染

Java Email 发HTML邮件工具 采用 freemarker模板引擎 1.常用方式对比 Java发送邮件有很多的实现方式 第一种&#xff1a;Java 原生发邮件mail.jar和activation.jar <!-- https://mvnrepository.com/artifact/javax.mail/mail --> <dependency><groupId>jav…...

CNI 网络流量分析(六)Calico 介绍与原理(二)

文章目录CNI 网络流量分析&#xff08;六&#xff09;Calico 介绍与原理&#xff08;二&#xff09;CNIIPAM指定 IP指定非 IPAM IPCNI 网络流量分析&#xff08;六&#xff09;Calico 介绍与原理&#xff08;二&#xff09; CNI 支持多种 datapath&#xff0c;默认是 linuxDa…...

短视频标题的几种类型和闭坑注意事项

目录 短视频标题的几种类型 1、悬念式 2、蹭热门式 3、干货式 4、对比式方法 5、总分/分总式方法 6、挑战式方式 7、启发激励式 8、讲故事式 02注意事项 1、避免使用冷门、生僻词汇 标题是点睛之笔&#xff0c;核心是视频内容 短视频标题的几种类型 1、悬念式 通过…...

操作系统——1.操作系统的概念、定义和目标

目录 1.概念 1.1 操作系统的种类 1.2电脑的组成 1.3电脑组成的介绍 1.4操作系统的概念&#xff08;定义&#xff09; 2.操作系统的功能和目标 2.1概述 2.2 操作系统作为系统资源的管理者 2.3 操作系统作为用户和计算机硬件间的接口 2.3.1用户接口的解释 2.3.2 GUI 2.3.3接…...

【html弹框拖拽和div拖拽功能】原生html页面引入vue语法后通过自定义指令简单实现div和弹框拖拽功能

前言 这是html版本的。只是引用了vue的语法。 这是很多公司会出现的一种情况&#xff0c;就是原生的页面&#xff0c;引入vue的语法开发 这就导致有些vue上很简单的功能。放到这里需要转换一下 以前写过一个vue版本的帖子&#xff0c;现在再加一个html版本的。 另一个vue版本…...

2023新华为OD机试题 - 计算网络信号(JavaScript) | 刷完必过

计算网络信号 题目 网络信号经过传递会逐层衰减,且遇到阻隔物无法直接穿透,在此情况下需要计算某个位置的网络信号值。 注意:网络信号可以绕过阻隔物 array[m][n] 的二维数组代表网格地图,array[i][j] = 0代表 i 行 j 列是空旷位置;array[i][j] = x(x 为正整数)代表 i 行 …...

27.边缘系统的架构

文章目录27 Architecures for the Edge 边缘系统的架构27.1 The Ecosystem of Edge-Dominant Systems 边缘主导系统的生态系统27.2 Changes to the Software Development Life Cycle 软件开发生命周期的变化27.3 Implications for Architecture 对架构的影响27.4 Implications …...

机器学习强基计划8-1:图解主成分分析PCA算法(附Python实现)

目录0 写在前面1 为什么要降维&#xff1f;2 主成分分析原理3 PCA与SVD的联系4 Python实现0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理&#xff1b;“广”在分析多个机器学习模型&#xf…...

Hudi-集成Spark之spark-shell 方式

Hudi集成Spark之spark-shell 方式 启动 spark-shell &#xff08;1&#xff09;启动命令 #针对Spark 3.2 spark-shell \--conf spark.serializerorg.apache.spark.serializer.KryoSerializer \--conf spark.sql.catalog.spark_catalogorg.apache.spark.sql.hudi.catalog.Hoo…...

Python爬虫:从js逆向了解西瓜视频的下载链接的生成

前言 最近花费了几天时间,想获取西瓜视频这个平台上某个视频的下载链接,运用js逆向进行获取。其实,如果小编一开始就注意到这一点(就是在做js逆向时,打了断点之后,然后执行相关代码,查看相关变量的值,结果一下子就蹦出很多视频相关的数据,查看了网站下的相关api链接,也…...

Numpy-如何对数组进行切割

前言 本文是该专栏的第24篇,后面会持续分享python的数据分析知识,记得关注。 继上篇文章,详细介绍了使用numpy对数组进行叠加。本文再详细来介绍,使用numpy如何对数组进行切割。说句题外话,前面有重点介绍numpy的各个知识点。 感兴趣的同学,可查看笔者之前写的详细内容…...

Python之字符串精讲(下)

前言 今天继续讲解字符串下半部分&#xff0c;内容包括字符串的检索、大小写转换、去除字符串中空格和特殊字符。 一、检索字符串 在Python中&#xff0c;字符串对象提供了很多用于字符串查找的方法&#xff0c;主要给大家介绍以下几种方法。 1. count() 方法 count() 方法…...

Python图像卡通化animegan2-pytorch实例演示

先看下效果图&#xff1a; 左边是原图&#xff0c;右边是处理后的图片&#xff0c;使用的 face_paint_512_v2 模型。 项目获取&#xff1a; animegan2-pytorch 下载解压后 cmd 可进入项目地址的命令界面。 其中 img 是我自己建的&#xff0c;用于存放图片。 需要 torch 版本 …...

谢希仁版《计算机网络》期末总复习【完结】

文章目录说明第一章 计算机网络概述计算机网络和互联网网络边缘网络核心分组交换网的性能网络体系结构控制平面和数据平面第二章 IP地址分类编址子网划分无分类编址特殊用途的IP地址IP地址规划和分配第三章 应用层应用层协议原理万维网【URL / HTML / HTTP】域名系统DNS动态主机…...

问:React的useState和setState到底是同步还是异步呢?

先来思考一个老生常谈的问题&#xff0c;setState是同步还是异步? 再深入思考一下&#xff0c;useState是同步还是异步呢&#xff1f; 我们来写几个 demo 试验一下。 先看 useState 同步和异步情况下&#xff0c;连续执行两个 useState 示例 function Component() {const…...

深度理解机器学习16-门控循环单元

评估简单循环神经网络的缺点。 描述门控循环单元&#xff08;Gated Recurrent Unit&#xff0c;GRU&#xff09;的架构。 使用GRU进行情绪分析。 将GRU应用于文本生成。 基本RNN通常由输入层、输出层和几个互连的隐藏层组成。最简单的RNN有一个缺点&#xff0c;那就是它们不…...

Python中Generators教程

要想创建一个iterator&#xff0c;必须实现一个有__iter__()和__next__()方法的类&#xff0c;类要能够跟踪内部状态并且在没有元素返回的时候引发StopIteration异常. 这个过程很繁琐而且违反直觉.Generator能够解决这个问题. python generator是一个简单的创建iterator的途径…...

数据结构与算法基础-学习-10-线性表之栈的清理、销毁、压栈、弹栈

一、函数实现1、ClearSqStack&#xff08;1&#xff09;用途清理栈的空间。只需要栈顶指针和栈底指针相等&#xff0c;就说明栈已经清空&#xff0c;后续新入栈的数据可以直接覆盖&#xff0c;不用实际清理数据&#xff0c;提升了清理效率。&#xff08;2&#xff09;源码Statu…...

Leetcode 每日一题 1234. 替换子串得到平衡字符串

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…...

【MYSQL中级篇】数据库数据查询学习

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 相关文章 文章名文章地址【MYSQL初级篇】入门…...

RTX4090D大模型推理专用镜像体验:Qwen-Image预装环境,一键启动图文对话

RTX4090D大模型推理专用镜像体验&#xff1a;Qwen-Image预装环境&#xff0c;一键启动图文对话 1. 镜像概述与核心优势 1.1 为什么选择专用镜像 在本地部署大语言模型时&#xff0c;环境配置往往是最耗时的环节。以Qwen-VL这样的视觉语言模型为例&#xff0c;需要处理CUDA版…...

macOS HTTPS资源嗅探配置指南:res-downloader从入门到精通

macOS HTTPS资源嗅探配置指南&#xff1a;res-downloader从入门到精通 【免费下载链接】res-downloader 资源下载器、网络资源嗅探&#xff0c;支持微信视频号下载、网页抖音无水印下载、网页快手无水印视频下载、酷狗音乐下载等网络资源拦截下载! 项目地址: https://gitcode…...

短效与动态代理IP区别,时效特性是关键

短效代理IP和动态代理IP最直观、最核心的区别&#xff0c;在于IP存活时长的时效特性&#xff0c;这也是两者适配不同场景的关键的原因。很多用户选型失误&#xff0c;本质是没有吃透两者的时效差异&#xff0c;导致所选代理无法匹配自身业务需求&#xff0c;出现IP失效快、资源…...

SMART-AM40玩转轻量桌面:Armbian下xfce4从安装到远程控制的完整指南

SMART-AM40轻量化桌面革命&#xff1a;Armbian系统下xfce4环境全流程部署与远程控制实战 在单板计算机领域&#xff0c;SMART-AM40凭借其Rockchip处理器和出色的能效比&#xff0c;正成为轻量化桌面解决方案的新宠。本文将带您完成从Armbian系统基础配置到xfce4桌面环境部署&am…...

颠覆式开源工具GHelper:极简华硕笔记本硬件控制解决方案

颠覆式开源工具GHelper&#xff1a;极简华硕笔记本硬件控制解决方案 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and other models 项目地…...

AudioSeal Pixel Studio实战教程:与LangChain音频处理Agent集成

AudioSeal Pixel Studio实战教程&#xff1a;与LangChain音频处理Agent集成 1. 工具介绍与核心价值 AudioSeal Pixel Studio是一款基于Meta开源的AudioSeal算法构建的专业音频水印工具。它能够在保持原始音质几乎不变的情况下&#xff0c;为音频文件嵌入隐形数字水印&#xf…...

Qt状态机实战指南:从基础到高级应用

1. Qt状态机基础入门 第一次接触Qt状态机时&#xff0c;我完全被它的设计哲学惊艳到了。想象一下你家的智能电饭煲&#xff1a;待机、煮饭、保温就是三个典型状态&#xff0c;按下按钮就是触发状态转换的信号——这就是状态机最接地气的理解方式。Qt中的QStateMachine框架&…...

Java Stream 中间操作全解析:惰性求值、无状态与有状态操作详解

一、前言 Stream API是Java 8的灵魂特性之一,它彻底改变了集合操作的写法——告别嵌套循环、简化逻辑判断,让代码更简洁、更易读、更高效。 但很多开发者刚接触Stream时,都会陷入一个误区:写了一串中间操作,却发现程序没有任何执行效果。其实核心原因很简单:Stream的中…...

【 MySQL 】第三节 - 约束实战全攻略

&#x1f31f;【深度剖析】MySQL 约束实战全攻略&#xff1a;从建表到外键行为管理&#xff08;附避坑指南&#xff09; 前言 在数据库设计中&#xff0c;约束&#xff08;Constraint&#xff09; 是保障数据一致性、完整性和业务逻辑性的“安全锁”。日前我系统学习了 MySQL…...

Jira替代工具如何选?2026年推荐十款适合小团队且容易上手项目管理平台

在数字化转型浪潮席卷全球的背景下&#xff0c;企业尤其是科技驱动型组织正加速将敏捷与精益理念融入核心运营流程。根据Gartner发布的报告&#xff0c;到2025年&#xff0c;超过80%的软件项目将采用敏捷或混合开发模式&#xff0c;这使得能够支撑高效协作与透明化管理的项目管…...