SpringBoot:邮件发送

        官网文档:39. Sending Email (spring.io)。

Sending Email

Spring框架提供了JavaMailSender实例,用于发送邮件。

如果SpringBoot项目中包含了相关的启动器,那么就会自动装配一个Bean实例到项目中。

        在SpringBoot项目中引入如下Email启动器,

<!-- spring-boot-starter-mail-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
</dependency>

核心接口和类

核心接口:MailSender

        org.springframework.mail是邮件发送依赖的顶级包,其中提供了邮件发送的核心接口MailSender,该接口提供了两个方法,分别用于指定逐个邮件发送、批量邮件发送。

package org.springframework.mail;/*** This interface defines a strategy for sending simple mails. Can be* implemented for a variety of mailing systems due to the simple requirements.* For richer functionality like MIME messages, consider JavaMailSender.** <p>Allows for easy testing of clients, as it does not depend on JavaMail's* infrastructure classes: no mocking of JavaMail Session or Transport necessary.*/
public interface MailSender {/*** Send the given simple mail message.-发送简单邮件* @param simpleMessage the message to send* @throws MailParseException in case of failure when parsing the message* @throws MailAuthenticationException in case of authentication failure* @throws MailSendException in case of failure when sending the message*/void send(SimpleMailMessage simpleMessage) throws MailException;/*** Send the given array of simple mail messages in batch.-批量发送一组简单邮件* @param simpleMessages the messages to send* @throws MailParseException in case of failure when parsing a message* @throws MailAuthenticationException in case of authentication failure* @throws MailSendException in case of failure when sending a message*/void send(SimpleMailMessage... simpleMessages) throws MailException;}

        可以看到,其实JavaMailSender接口也作为它的子接口存在,并且内置了一个实现类JavaMailSenderImpl可以供我们直接使用。

邮件消息接口:MailMessage

        邮件消息接口:规定一封电子邮件对应的组成元素,内置了简单邮件消息和媒体邮件消息两个实现子类。

package org.springframework.mail;import java.util.Date;/*** This is a common interface for mail messages, allowing a user to set key* values required in assembling a mail message, without needing to know if* the underlying message is a simple text message or a more sophisticated* MIME message.** <p>Implemented by both SimpleMailMessage and MimeMessageHelper,* to let message population code interact with a simple message or a* MIME message through a common interface.*/
public interface MailMessage {void setFrom(String from) throws MailParseException;void setReplyTo(String replyTo) throws MailParseException;void setTo(String to) throws MailParseException;void setTo(String... to) throws MailParseException;void setCc(String cc) throws MailParseException;void setCc(String... cc) throws MailParseException;void setBcc(String bcc) throws MailParseException;void setBcc(String... bcc) throws MailParseException;void setSentDate(Date sentDate) throws MailParseException;void setSubject(String subject) throws MailParseException;void setText(String text) throws MailParseException;}

简单邮件对象:SimpleMailMessage 

        可以用来囊括简单邮件信息的类是SimpleMailMessage类,邮件主体内容以简单文本形式为主。

A simple value object that encapsulates the properties of a simple mail such as from and to (plus many others) is the SimpleMailMessage class. 

        该类的源码如下, 

package org.springframework.mail;import java.io.Serializable;
import java.util.Date;import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;/*** Models a simple mail message, including data such as the from, to, cc, subject,* and text fields.** <p>Consider {@code JavaMailSender} and JavaMail {@code MimeMessages} for creating* more sophisticated messages, for example messages with attachments, special* character encodings, or personal names that accompany mail addresses.** @author Dmitriy Kopylenko* @author Juergen Hoeller* @since 10.09.2003* @see MailSender* @see org.springframework.mail.javamail.JavaMailSender* @see org.springframework.mail.javamail.MimeMessagePreparator* @see org.springframework.mail.javamail.MimeMessageHelper* @see org.springframework.mail.javamail.MimeMailMessage*/
@SuppressWarnings("serial")
public class SimpleMailMessage implements MailMessage, Serializable {@Nullableprivate String from;@Nullableprivate String replyTo;@Nullableprivate String[] to;@Nullableprivate String[] cc;@Nullableprivate String[] bcc;@Nullableprivate Date sentDate;@Nullableprivate String subject;@Nullableprivate String text;/*** Create a new {@code SimpleMailMessage}.*/public SimpleMailMessage() {}/*** Copy constructor for creating a new {@code SimpleMailMessage} from the state* of an existing {@code SimpleMailMessage} instance.*/public SimpleMailMessage(SimpleMailMessage original) {Assert.notNull(original, "'original' message argument must not be null");this.from = original.getFrom();this.replyTo = original.getReplyTo();this.to = copyOrNull(original.getTo());this.cc = copyOrNull(original.getCc());this.bcc = copyOrNull(original.getBcc());this.sentDate = original.getSentDate();this.subject = original.getSubject();this.text = original.getText();}@Overridepublic void setFrom(String from) {this.from = from;}@Nullablepublic String getFrom() {return this.from;}@Overridepublic void setReplyTo(String replyTo) {this.replyTo = replyTo;}@Nullablepublic String getReplyTo() {return this.replyTo;}@Overridepublic void setTo(String to) {this.to = new String[] {to};}@Overridepublic void setTo(String... to) {this.to = to;}@Nullablepublic String[] getTo() {return this.to;}@Overridepublic void setCc(String cc) {this.cc = new String[] {cc};}@Overridepublic void setCc(String... cc) {this.cc = cc;}@Nullablepublic String[] getCc() {return this.cc;}@Overridepublic void setBcc(String bcc) {this.bcc = new String[] {bcc};}@Overridepublic void setBcc(String... bcc) {this.bcc = bcc;}@Nullablepublic String[] getBcc() {return this.bcc;}@Overridepublic void setSentDate(Date sentDate) {this.sentDate = sentDate;}@Nullablepublic Date getSentDate() {return this.sentDate;}@Overridepublic void setSubject(String subject) {this.subject = subject;}@Nullablepublic String getSubject() {return this.subject;}@Overridepublic void setText(String text) {this.text = text;}@Nullablepublic String getText() {return this.text;}/*** Copy the contents of this message to the given target message.* @param target the {@code MailMessage} to copy to*/public void copyTo(MailMessage target) {Assert.notNull(target, "'target' MailMessage must not be null");if (getFrom() != null) {target.setFrom(getFrom());}if (getReplyTo() != null) {target.setReplyTo(getReplyTo());}if (getTo() != null) {target.setTo(copy(getTo()));}if (getCc() != null) {target.setCc(copy(getCc()));}if (getBcc() != null) {target.setBcc(copy(getBcc()));}if (getSentDate() != null) {target.setSentDate(getSentDate());}if (getSubject() != null) {target.setSubject(getSubject());}if (getText() != null) {target.setText(getText());}}@Overridepublic boolean equals(@Nullable Object other) {if (this == other) {return true;}if (!(other instanceof SimpleMailMessage)) {return false;}SimpleMailMessage otherMessage = (SimpleMailMessage) other;return (ObjectUtils.nullSafeEquals(this.from, otherMessage.from) &&ObjectUtils.nullSafeEquals(this.replyTo, otherMessage.replyTo) &&ObjectUtils.nullSafeEquals(this.to, otherMessage.to) &&ObjectUtils.nullSafeEquals(this.cc, otherMessage.cc) &&ObjectUtils.nullSafeEquals(this.bcc, otherMessage.bcc) &&ObjectUtils.nullSafeEquals(this.sentDate, otherMessage.sentDate) &&ObjectUtils.nullSafeEquals(this.subject, otherMessage.subject) &&ObjectUtils.nullSafeEquals(this.text, otherMessage.text));}@Overridepublic int hashCode() {int hashCode = ObjectUtils.nullSafeHashCode(this.from);hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.replyTo);hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.to);hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.cc);hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.bcc);hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.sentDate);hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.subject);return hashCode;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder("SimpleMailMessage: ");sb.append("from=").append(this.from).append("; ");sb.append("replyTo=").append(this.replyTo).append("; ");sb.append("to=").append(StringUtils.arrayToCommaDelimitedString(this.to)).append("; ");sb.append("cc=").append(StringUtils.arrayToCommaDelimitedString(this.cc)).append("; ");sb.append("bcc=").append(StringUtils.arrayToCommaDelimitedString(this.bcc)).append("; ");sb.append("sentDate=").append(this.sentDate).append("; ");sb.append("subject=").append(this.subject).append("; ");sb.append("text=").append(this.text);return sb.toString();}@Nullableprivate static String[] copyOrNull(@Nullable String[] state) {if (state == null) {return null;}return copy(state);}private static String[] copy(String[] state) {return state.clone();}}

媒体邮件对象:MimeMailMessage

        MemeMailMessage表示媒体邮件对象,可以实现自定义邮件模板的动态填充和邮件发送。

/** Copyright 2002-2017 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.mail.javamail;import java.util.Date;import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;import org.springframework.mail.MailMessage;
import org.springframework.mail.MailParseException;/*** Implementation of the MailMessage interface for a JavaMail MIME message,* to let message population code interact with a simple message or a MIME* message through a common interface.** <p>Uses a MimeMessageHelper underneath. Can either be created with a* MimeMessageHelper instance or with a JavaMail MimeMessage instance.** @author Juergen Hoeller* @since 1.1.5* @see MimeMessageHelper* @see javax.mail.internet.MimeMessage*/
public class MimeMailMessage implements MailMessage {private final MimeMessageHelper helper;/*** Create a new MimeMailMessage based on the given MimeMessageHelper.* @param mimeMessageHelper the MimeMessageHelper*/public MimeMailMessage(MimeMessageHelper mimeMessageHelper) {this.helper = mimeMessageHelper;}/*** Create a new MimeMailMessage based on the given JavaMail MimeMessage.* @param mimeMessage the JavaMail MimeMessage*/public MimeMailMessage(MimeMessage mimeMessage) {this.helper = new MimeMessageHelper(mimeMessage);}/*** Return the MimeMessageHelper that this MimeMailMessage is based on.*/public final MimeMessageHelper getMimeMessageHelper() {return this.helper;}/*** Return the JavaMail MimeMessage that this MimeMailMessage is based on.*/public final MimeMessage getMimeMessage() {return this.helper.getMimeMessage();}@Overridepublic void setFrom(String from) throws MailParseException {try {this.helper.setFrom(from);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setReplyTo(String replyTo) throws MailParseException {try {this.helper.setReplyTo(replyTo);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setTo(String to) throws MailParseException {try {this.helper.setTo(to);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setTo(String... to) throws MailParseException {try {this.helper.setTo(to);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setCc(String cc) throws MailParseException {try {this.helper.setCc(cc);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setCc(String... cc) throws MailParseException {try {this.helper.setCc(cc);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setBcc(String bcc) throws MailParseException {try {this.helper.setBcc(bcc);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setBcc(String... bcc) throws MailParseException {try {this.helper.setBcc(bcc);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setSentDate(Date sentDate) throws MailParseException {try {this.helper.setSentDate(sentDate);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setSubject(String subject) throws MailParseException {try {this.helper.setSubject(subject);}catch (MessagingException ex) {throw new MailParseException(ex);}}@Overridepublic void setText(String text) throws MailParseException {try {this.helper.setText(text);}catch (MessagingException ex) {throw new MailParseException(ex);}}}

异常接口:MailException

 This package also contains a hierarchy of checked exceptions that provide a higher level of abstraction over the lower level mail system exceptions, with the root exception being MailException.

        Mail是邮件发送的异常处理根接口,源码如下,

@SuppressWarnings("serial")
public abstract class MailException extends NestedRuntimeException {/*** Constructor for MailException.* @param msg the detail message*/public MailException(String msg) {super(msg);}/*** Constructor for MailException.* @param msg the detail message* @param cause the root cause from the mail API in use*/public MailException(@Nullable String msg, @Nullable Throwable cause) {super(msg, cause);}}

        当然,它也内置了一系列子接口,

        基本含义如下, 

MailSendException:邮件发送失败异常

MailParseException:非法的邮件配置属性

MailPreparationException:邮件模板渲染出错时,会抛出此类异常。

MailAuthenticationException:认证失败异常。

application.yml配置

        要使用JavaMailSender实现邮件发送,那么就需要先对其进行配置。通过上述内容的初步了解,基本上可以确定我们要配置的参数就和JavaMailSenderImpl实现子类相关了,其成员属性如下,和SpringBoot官网配置文档给出的配置参数也是一一对应的。

JavaMailSenderImpl实现子类
springBoot官网配置文档

        我所做的配置如下,

spring:mail:protocol: smtphost: smtp.qq.comdefault-encoding: UTF-8username: email-accountpassword: key/passwordproperties:mail:debug: true #开启日志打印smtp:auth: truestarttls:enable: truerequired: true

邮件发送

        上面了解到两种邮件形式,所以,接下来我们尝试一下,如何发送简单文本邮件,以及自定义模板的邮件。

SimpleMailMessage发送

        简单邮件的发送示例代码如下,其中:JavaMailSender 实例即为文章开头部分提到的,由Spring框架自动注入的Bean实例。


@Component
public class EmailSendServiceImpl implements EmailSenderService {@Autowiredprivate JavaMailSender mailSender;@Overridepublic R sendSimpleEmail(String from, String to, String subject, String text) {try {//创建邮件对象SimpleMailMessage mailMessage = new SimpleMailMessage();//设置邮件属性mailMessage.setFrom(from);//发件人mailMessage.setTo(to);//收件人mailMessage.setSubject(subject);//主题mailMessage.setText(text);//文本内容mailMessage.setSentDate(new Date());//发送日期//发送邮件this.mailSender.send(mailMessage);//返回消息return R.ok("邮件发送成功!");} catch (MailException e) {e.printStackTrace();return R.fail("邮件发送失败!");}}
}

MimeMailMessage发送

        为了方便向邮件中写入自定义内容,所以自定义邮件模板需要借助后端的模板引擎来实现,此处我们选择thymeleaf模板引擎。以下为此时的依赖,

       <!-- spring-boot-starter-mail--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>

thymeleaf配置

         如上图所示,thymeleaf模板引擎内置了一套配置规则,最简单的方式就是将其原模原样的写到application.yml配置文件中即可。

        以下为我的配置项,

spring:thymeleaf:prefix: classpath:/templates/suffix: .htmlmode: HTML

邮件模板定制

邮件模板定制,其实就是写一个前端页面,然后借助thymeleaf模板引擎的表达式语法,实现自定义内容的动态填充即可。

关于Thymeleaf的表达式语法,详情可参阅官网文档:Tutorial: Using Thymeleaf

        但是说实话,对于简单的文本替换,以下的简单表达式语法基本就能满足了。

 关于邮件模板的定制,如果要求特别高的话,可以自己从零开始写;但是,如果要快速实现的话,也可以借助在线生成工具,这里比较推荐:拉易网提供的精美邮件定制服务,里面内置了一些可以直接使用的邮件模板,也可以在此基础上进行个性化定制,零代码自动生成。

最终将定制好的模板下载下来即可。

       

 如何使用邮件模板

        那么我们下载下来的邮件模板如何使用呢?

其实和我们的Thymeleaf模板引擎配置相关,毕竟是要将这个模板交给Thymeleaf模板引擎进行值的动态替换和渲染的。

这里对应于thymeleaf的prefix配置项,我的email.html邮件模板就放在templates路径下。

        另外,为了实现动态替换文本,例如:我这里想要实现邮件验证码,那么,我就要通过表达式语法,提供一个文本字符串的变量verifyCode,以便于在后面发送邮件时,将这个变量替换为任何我们随机生成的验证码字符串。

示例代码

终于来到代码环节了,但是前面的环节也确实缺一不可。

@Component
public class EmailSendServiceImpl implements EmailSenderService {@Autowiredprivate JavaMailSender mailSender;@Autowiredprivate TemplateEngine templateEngine;@Overridepublic R sendMimeEmail(String from, String to, String subject) {//create a new JavaMail MimeMessageMimeMessage mimeMailMessage = this.mailSender.createMimeMessage();MimeMessageHelper mimeMessageHelper = null;try {/*** mimeMessage – the mime message to work on* multipart – whether to create a multipart message that supports alternative texts, inline elements and attachments (corresponds to MULTIPART_MODE_MIXED_RELATED)*/mimeMessageHelper = new MimeMessageHelper(mimeMailMessage, true);//setting basic paramsmimeMessageHelper.setFrom(from);mimeMessageHelper.setTo(to);mimeMessageHelper.setSubject(subject);//create html-text based on thymeleaf templateContext context = new Context();context.setVariable("verifyCode","456935");String process = templateEngine.process("email", context);//设置邮件内容/*** process:the text for the message* html:whether to apply content type "text/html" for an HTML mail, using default content type ("text/plain") else*/mimeMessageHelper.setText(process,true);//发送邮件this.mailSender.send(mimeMailMessage);return R.ok("邮件发送成功!");} catch (MessagingException e) {e.printStackTrace();return R.fail("邮件发送失败!");}}
}

* 模板变量替换:Context探究

        先看一下源码注释,

IContext接口的非web(场景)实现,适用于非web场景。

        我们继续查看Context父类AbstractContext实现的IContext接口,可以看到,有一个我们刚才用于动态替换thymeleaf模板变量的方法,

        注意到它还提供了一个重载方法,可以实现多个模板变量值的设置,都是以key-value键值对的形式出现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/171575.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据分享】2019-2023年我国区县逐月新房房价数据(Excel/Shp格式)

房价是一个城市发展程度的重要体现&#xff0c;一个城市的房价越高通常代表这个城市越发达&#xff0c;对于人口的吸引力越大&#xff01;因此&#xff0c;房价数据是我们在各项城市研究中都非常常用的数据&#xff01;之前我们分享过2019-2023年我国地级市逐月房价数据&#x…

Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap

在 Spring Boot 项目中&#xff0c;我们经常使用 YAML 文件来配置应用程序的属性。在这篇博客中&#xff0c;我将模拟如何在 Java 的 Spring Boot 项目中读取 YAML 文件中的数组、集合和 HashMap。 1. 介绍 YAML&#xff08;YAML Aint Markup Language&#xff09;是一种人类…

【Spring集成MyBatis】MyBatis注解开发

文章目录 1. MyBatis的常用注解2. 基于注解的MyBatis增删改查增删改查完整代码加载映射关系测试代码 3. MyBatis的注解实现复杂映射开发一对一操作的实现一对一操作实现的第二种方式一对多操作的实现多对多操作实现 1. MyBatis的常用注解 2. 基于注解的MyBatis增删改查 使用注…

Linux加强篇004-Vim编辑器与Shell命令脚本

目录 前言 1. Vim文本编辑器 1.1 编写简单文档 1.2 配置主机名称 1.3 配置网卡信息 1.4 配置软件仓库 2. 编写Shell脚本 2.1 编写简单的脚本 2.2 接收用户的参数 2.3 判断用户的参数 3. 流程控制语句 3.1 if条件测试语句 3.2 for条件循环语句 3.3 while条件循环语…

【开源】基于JAVA的高校学院网站

项目编号&#xff1a; S 020 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S020&#xff0c;文末获取源码。} 项目编号&#xff1a;S020&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学院院系模块2.2 竞赛报名模块2.3 教…

Postman如何使用(三):使用数据文件

数据文件是非常强大的方式使用不同的测试数据来测试我们的API&#xff0c;以检查它们是否在各种情况下都能正常运行。我们可以认为数据文件是“Collection Runner”中每个请求的参数。下面&#xff0c;我们通过一个例子来说明如何使用数据文件。 这篇文章需要结合下面两个文件进…

史上最全前端知识点+高频面试题合集,十二大专题,命中率高达95%

前言&#xff1a; 下面分享一些关于阿里&#xff0c;美团&#xff0c;深信服等公司的面经&#xff0c;供大家参考一下。大家也可以去收集一些其他的面试题&#xff0c;可以通过面试题来看看自己有哪里不足。也可以了解自己想去的公司会问什么问题&#xff0c;进行有针对的复习。…

《数据结构与算法之美》读书笔记2

链表操作的技巧 1.理解指针 将摸个变量赋值给指针&#xff0c;实际上就是将这个变量的地址赋给指针&#xff0c;或者&#xff0c;指针中存储了这个变量的地址&#xff0c;指向了这个变量&#xff0c;所以可以通过指针找到这个变量。 2.内存泄漏或指针丢失 删除链表节点时&a…

人工智能|机器学习——循环神经网络的简洁实现

循环神经网络的简洁实现 如何使用深度学习框架的高级API提供的函数更有效地实现相同的语言模型。 我们仍然从读取时光机器数据集开始。 import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2lbatch_size, num_steps 32, 35 t…

itop4412移植lrzsz工具踩坑笔记

4412开发板在传输文件一直用的都是tftp文件传输&#xff0c;但这样效率有点慢&#xff0c;平常在linux上习惯用lrzsz工具来传输文件&#xff0c;特此记录下&#xff0c;因为不熟悉linux编译 踩坑了很多地方 在操作前 我们的虚拟机要线安装好编译环境 下载lrzsz源码&#xff0…

一起学docker系列之十docker安装tomcat实践

目录 前言1 安装tomcat的步骤步骤 1: 查找并拉取 Tomcat 镜像步骤 2: 运行 Tomcat 容器步骤 3: 管理 Tomcat 容器步骤 4: 访问 Tomcat 首页 2 解决访问首页的404访问不到的问题2.1 Tomcat 10 的默认设置问题2.2 端口映射或防火墙问题 3 推荐使用 Tomcat 8.5 版本总结 前言 当安…

【华为OD题库-037】跳房子2-java

题目 跳房子&#xff0c;也叫跳飞机&#xff0c;是一种世界性的儿童游戏游戏。参与者需要分多个回合按顺序跳到第1格直到房子的最后一格&#xff0c;然后获得一次选房子的机会&#xff0c;直到所有房子被选完&#xff0c;房子最多的人获胜。 跳房子的过程中&#xff0c;如果有踩…

【Docker】从零开始:11.Harbor搭建企业镜像仓库

【Docker】从零开始&#xff1a;11.Harbor搭建企业镜像仓库 1. Harbor介绍2. 软硬件要求(1). 硬件要求(2). 软件要求 3.Harbor优势4.Harbor的误区5.Harbor的几种安装方式6.在线安装(1).安装composer(2).配置内核参数,开启路由转发(3).下载安装包并解压(4).创建并修改配置文件(5…

python+pytest接口自动化(1)-接口测试基础

一般我们所说的接口即API&#xff0c;那什么又是API呢&#xff0c;百度给的定义如下&#xff1a; API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的接口&#xff08;如函数、HTTP接口&#xff09;&#xff0c;或指软件系…

3款免费的语音视频转文本AI神器

最近有很多粉丝让我出一期关于语音转文本的免费AI神器&#xff0c;毕竟这类工具在学习和工作中经常会用到&#xff0c;那今天就给大家安排。 我亲测了好几款软件之后&#xff0c;最终评选留下了三款 剪映hugging face飞书妙记 接下来一一给大家讲解 1.剪映 剪映其实是一款视…

引用、动态内存分配、函数、结构体

引用 定义和初始化 **数据类型 &引用名 目标名;**引用和目标共用同一片空间&#xff08;相当于对一片空间取别名&#xff09;。 引用的底层实现&#xff1a;数据类型 * const p&#xff1b; ------> 常指针 int const *p; -----> 修饰 *p const int *p; ----->…

stm32实现0.96oled图片显示,菜单功能

stm32实现0.96oled图片显示&#xff0c;菜单功能 功能展示简介代码介绍oled.coled.holedfont.h&#xff08;字库文件&#xff09;main函数 代码思路讲解 本期内容&#xff0c;我们将学习0.96寸oled的进阶使用&#xff0c;展示图片&#xff0c;实现菜单切换等功能&#xff0c;关…

QT visual stdio加载动态库报错126问题

报错126是找不到指定的模块 QT 查看构建目录&#xff0c;将依赖的动态库放到该目录下即可成功 visual stdio将依赖的动态库放到运行目录 在vs中使用导出类的动态库时&#xff0c;不但需要将对应的.dll放到对应的目录下&#xff0c;还需要将该动态库对应的.lib添加到如下配置才…

【SAS Planet 下载地图瓦片】

SAS Planet是一位俄罗斯爱好者创建的的开源应用&#xff0c;该应用可以浏览与下载主流网络地图&#xff0c;包括Google地图、Google地球、Bing地图、Esri 地图、Yandex地图等&#xff0c;支持100多图源。 安装包下载地址&#xff1a;https://www.sasgis.org/download/ github…

337. 打家劫舍III (二叉树)

题目 题解 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def rob(self, root: Optional[TreeNode]) ->…