maven依赖
<!--邮箱验证-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
yml文件配置
spring:
mail: #邮箱
default-encoding: UTF-8
host: smtp.qq.com
username: 你的QQ邮箱@qq.com #发送邮件的邮箱
password: xxxxxxxxxxx #发送邮件邮箱的KEY,可以去百度一下邮箱如何开启stmp 就知道了
port: 465
auth: true
properties:
mail.smtp.socketFactory.fallback: false
mail.smtp.socketFactory.port: 465
mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory
邮件发送实体
package com.ruoyi.email.entity;
import lombok.Data;
@Data
public class EmailEntity {
private String sendTo;
private String title;
private String detail;
private String fileList;
}
请求响应对象AjaxResult类
package com.ruoyi.common.core.domain;
import cn.hutool.http.HttpStatus;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ApiModel("请求响应对象")
public class AjaxResult<T> {
private static final long serialVersionUID = 1L;
@ApiModelProperty("消息状态码")
private int code;
@ApiModelProperty("消息内容")
private String msg;
@ApiModelProperty("数据对象")
private T data;
public AjaxResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static AjaxResult<Void> success() {
return AjaxResult.success("操作成功");
}
public static <T> AjaxResult<T> success(T data) {
return AjaxResult.success("操作成功", data);
}
public static AjaxResult<Void> success(String msg) {
return AjaxResult.success(msg, null);
}
public static <T> AjaxResult<T> success(String msg, T data) {
return new AjaxResult<>(HttpStatus.HTTP_OK, msg, data);
}
public static AjaxResult<Void> error() {
return AjaxResult.error("操作失败");
}
public static AjaxResult<Void> error(String msg) {
return AjaxResult.error(msg, null);
}
public static <T> AjaxResult<T> error(String msg, T data) {
return new AjaxResult<>(HttpStatus.HTTP_INTERNAL_ERROR, msg, data);
}
public static AjaxResult<Void> error(int code, String msg) {
return new AjaxResult<>(code, msg, null);
}
}
邮件发送业务类EmailService
package com.ruoyi.email.service;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.CommonUtil;
import com.ruoyi.email.entity.EmailEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamSource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
@Service
public class EmailService {
@Value("${spring.mail.username}")
private String username;
@Autowired
JavaMailSender mailSender;
public AjaxResult sendSimpleMail(EmailEntity emailEntity) {
try {
MimeMessage mailMessage=mailSender.createMimeMessage();
MimeMessageHelper helper=new MimeMessageHelper(mailMessage,true, Constants.UTF_8);
helper.setFrom(username);
helper.setTo(emailEntity.getSendTo());
helper.setSubject(emailEntity.getTitle());
helper.setText(emailEntity.getDetail());
if (!CommonUtil.isEmpty(emailEntity.getFileList())){
String[] fileList = emailEntity.getFileList().split(",");
for (String filePath:fileList) {
URL url = new URL(filePath);
DataSource dataSource=new URLDataSource(url);
String fileName=filePath.substring(filePath.lastIndexOf("/")+1);
helper.addAttachment(fileName,dataSource);
}
}
mailSender.send(mailMessage);
} catch (MessagingException e) {
e.printStackTrace();
}catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return AjaxResult.success("邮件发送成功");
}
}
邮件服务接口类EmailController
package com.ruoyi.web.controller.email;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.email.entity.EmailEntity;
import com.ruoyi.email.service.EmailService;
import io.swagger.annotations.Api;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/system/email")
@Api(tags = "邮件服务")
public class EmailController {
@Resource
EmailService emailService;
@RepeatSubmit()
@PostMapping("/sendEmail")
public AjaxResult verifyEmail(@Validated(EditGroup.class) @RequestBody EmailEntity emailEntity) {
return emailService.sendSimpleMail(emailEntity);
}
}
前端请求(Vue) js
import request from '@/utils/request'
export function sendEmail(data) {
return request({
url: '/system/email/sendEmail',
method: 'post',
data: data
})
}
前端Vue页面
<template>
<div class="app-container">
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="接收邮箱" prop="sendTo">
<el-input v-model="form.sendTo" placeholder="请输入接收邮箱" />
</el-form-item>
<el-form-item label="邮件标题" prop="title">
<el-input v-model="form.title" placeholder="请输入邮件标题" />
</el-form-item>
<el-form-item label="邮件内容" prop="detail">
<el-input v-model="form.detail" type="textarea" :rows="5"/>
</el-form-item>
<el-form-item label="附件" prop="fileList">
<file-upload v-model="form.fileList"></file-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer" style="float: right">
<el-button :loading="buttonLoading" type="primary" @click="submitForm">发 送</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</div>
</template>
<script>
import { sendEmail } from "@/api/system/email";
export default {
name: "SendEmail",
data() {
return {
buttonLoading: false,
loading: true,
form: {},
rules: {
sendTo: [
{required: true, message: "请输入收件箱", trigger: "blur"}
],
title: [
{required: true, message: "请输入邮件标题", trigger: "blur"}
],
detail: [
{required: true, message: "请输入邮件内容", trigger: "blur"}
],
}
};
},
methods: {
cancel() {
this.reset();
this.$emit('cancel',false);
},
reset(){
this.form = {}
},
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
sendEmail(this.form).then(response => {
this.$modal.msgSuccess(response.msg);
this.cancel();
}).finally(() => {
this.buttonLoading = false;
});
}
});
},
}
};
</script>
多文件上传组件FileUpload
<template>
<div class="upload-file">
<el-upload
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:file-list="fileList"
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="upload"
>
<!-- 上传按钮 -->
<el-button size="mini" type="primary">选取文件</el-button>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
请上传
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
的文件
</div>
</el-upload>
<!-- 文件列表 -->
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
<el-link :href="`${file.url}`" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<div class="ele-upload-list__item-content-action" v-if="isShowDelete">
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
</div>
</li>
</transition-group>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
export default {
name: "FileUpload",
props: {
value: [String, Object, Array],
isShowDelete: true,
limit: {
type: Number,
default: 5,
},
fileSize: {
type: Number,
default: 50,
},
fileType: {
type: Array,
default: () => ["doc", "xls", "ppt", "txt", "pdf","zip","rar","docs","msi","sql","jar","war","png", "jpg", "jpeg","gif"],
},
isShowTip: {
type: Boolean,
default: true
}
},
data() {
return {
baseUrl: process.env.VUE_APP_BASE_API,
uploadFileUrl: process.env.VUE_APP_BASE_API + "/system/oss/upload",
headers: {
Authorization: "Bearer " + getToken(),
},
fileList: [],
};
},
watch: {
value: {
handler(val) {
if (val) {
let temp = 1;
const list = Array.isArray(val) ? val : this.value.split(',');
this.fileList = list.map(item => {
if (typeof item === "string") {
item = { name: item, url: item };
}
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else {
this.fileList = [];
return [];
}
},
deep: true,
immediate: true
}
},
computed: {
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
},
},
methods: {
handleBeforeUpload(file) {
if (this.fileType) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
const isTypeOk = this.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
this.$message.error(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
return false;
}
}
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
return false;
}
}
return true;
},
handleExceed() {
this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
},
handleUploadError(err) {
this.$message.error("上传失败, 请重试");
},
handleUploadSuccess(res, file) {
if (res.code === 200) {
this.$message.success("上传成功");
this.fileList.push({ name: res.data.fileName, url: res.data.url });
this.$emit("input", this.listToString(this.fileList));
} else {
this.$message.error(res.msg);
this.loading.close();
}
},
handleDelete(index) {
this.fileList.splice(index, 1);
this.$emit("input", this.listToString(this.fileList));
},
getFileName(name) {
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1).toLowerCase();
} else {
return "";
}
},
listToString(list, separator) {
let strs = "";
separator = separator || ",";
for (let i in list) {
strs += list[i].url + separator;
}
return strs != "" ? strs.substr(0, strs.length - 1) : "";
},
},
};
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
}
</style>