--> <#-- --> <#--
--> <#--2022-07-22
--> <#-- 富文本中的内容 --> ${content}先看效果
注意点
1,:需要安装QRCode 组件,自己百度去
2:需要安装wangEditor富文本编辑器组件,版本5,请上官网自己找教程,找vue版本的,安装很复杂,貌似还有兼容问题,如果有问题在回复我,我用的是ruoyi3.7.0
搜索
重置
新增
修改
删除
{{ scope.row.picHyperlink }}
{{ parseTime(scope.row.modifyDt, '{y}-{m}-{d}') }}
{{ parseTime(scope.row.releaseTime, '{y}-{m}-{d}') }}
{{ parseTime(scope.row.lockTime, '{y}-{m}-{d}') }}
预览
修改
删除
{
form.picShow = (val == 'null' || !val) ? nextItem.picShow : val;
nextItem.picShow = form.picShow;
})"
>
显示
隐藏
{
form.pageType = (val == 'null' || !val) ? nextItem.pageType : val;
nextItem.pageType = form.pageType;
show.richText = form.pageType == '0'
})"
>
第三方
本地页面
{{ routeQueryParms.picstr }}
网页内容
{urlItem.htmlUrl = null}"
>
、
第二步,补齐配置文件
上述vue组件中使用到的wangEditor.js配置,请依据引入的路径创建本文件
import { getToken } from '@/utils/auth'
// import resize from "../views/dashboard/mixins/resize";
import { uploadFileHce } from '../api/common'
import { Message } from 'element-ui'
export default {
// menus: [
// // "head",
// 'bold',
// 'fontSize',
// "fontName",
// 'italic',
// 'underline',
// '|',
// 'strikeThrough',
// 'indent',
// 'lineHeight',
// 'foreColor',
// 'backColor',
// 'link',
// '|',
// 'list',
// // "todo",
// 'image',
// 'justify',
// 'quote',
// 'table',
// '|',
// 'code',
// 'splitLine',
// 'undo',
// 'emoticon',
// 'redo'
// ],
placeholder: '请输入内容...',
scroll: false,
// autoFocus:true,
MENU_CONF: {
// emotion:{
// emotions: ' '.split(' ') // 数组
// }
// fontFamily: {
// fontFamilyList: [
// // 元素支持两种形式
// // 1. 字符串;
// // 2. { name: 'xxx', value: 'xxx' }
//
// '黑体',
// '楷体',
// { name: '仿宋', value: '仿宋' },
// 'Arial',
// 'Tahoma',
// 'Verdana'
// ]
// },
// 上传图片的配置
uploadImage: {
server: process.env.VUE_APP_BASE_API + '/common/editorUpload/hce',
// 字段名称
fieldName: 'file',
// 单个文件的最大体积限制,默认为 2M
maxFileSize: 4 * 1024 * 1024, // 1M
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: 20,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['image/*'],
base64LimitSize: 75 * 1024, // 100kb 以下插入 base64
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
meta: {
type: 'img'
},
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: false,
// 自定义增加 http header
headers: {
// Accept: 'text/x-json',
Authorization: 'Bearer ' + getToken()
},
// 跨域是否传递 cookie ,默认为 false
withCredentials: false,
// 超时时间,默认为 10 秒
timeout: 5 * 1000, // 5 秒
onBeforeUpload(files) {
return files // 返回哪些文件可以上传
// return false 会阻止上传
},
// 上传进度的回调函数
onProgress(progress) {
// console.log("onProgress", progress);
},
// 单个文件上传成功之后
onSuccess(file, res) {
Message({
message: '上传成功',
type: 'success'
})
// console.log("onSuccess", file, res);
},
// 单个文件上传失败
onFailed(file, res) {
Message({
message: res.msg ? res.msg : '文件上传失败,可能是网络原因',
type: 'error'
})
// console.log("onFailed", file, res);
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
Message({
message: res.msg ? res.msg : '文件上传时发送错误,可能是网络原因,请检查!',
type: 'error'
})
}
// 用户自定义上传图片
// customUpload(file, insertFn) {
// const data = new FormData();
// data.append("file", file); // file 即选中的文件
// // data.append("type", 'img'); // file
//
// uploadFileHce(data).then(res => {
// // 这个返回的是相对路径
// let url = res.fileName;
// // baseapi进行拼接
// url = process.env.VUE_APP_BASE_API + url;
// insertFn(url, '', res.fileName); //插入图片
// })
// }
},
// 上传视频的配置
uploadVideo: {
server: process.env.VUE_APP_BASE_API + '/common/editorUpload/hce',
fieldName: 'file',
// 单个文件的最大体积限制,默认为 10M, 现在是20M
maxFileSize: 200 * 1024 * 1024, // 5M
// 最多可上传几个文件,默认为 5
maxNumberOfFiles: 3,
// 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['video/*'],
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: false,
meta: {
type: 'video'
},
// // 自定义增加 http header
headers: {
Authorization: 'Bearer ' + getToken()
},
// // 跨域是否传递 cookie ,默认为 false
withCredentials: false,
// // 超时时间,默认为 30 秒
timeout: 21 * 1000, // 15 秒
// 上传之前触发
onBeforeUpload(file) {
return file
},
// 上传进度的回调函数
onProgress(progress) {
// console.log("onProgress", progress);
},
// 单个文件上传成功之后
onSuccess(file, res) {
Message({
message: '上传成功',
type: 'success'
})
// console.log("onSuccess", file, res);
},
// 单个文件上传失败
onFailed(file, res) {
Message({
message: res.msg ? res.msg : '文件上传失败,可能是网络原因',
type: 'error'
})
// console.log("onFailed", file, res);
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
Message({
message: res.msg ? res.msg : '文件上传时发送错误,可能是网络原因,请检查!',
type: 'error'
})
}
// 用户自定义上传视频
// customUpload(file, insertFn) {
// const data = new FormData();
// data.append("file", file); // file 即选中的文件
// data.append("type", 'video'); // file 即选中的文件
//
// uploadFileHce(data).then(res => {
// // 这个返回的是相对路径
// let url = res.fileName;
// // baseapi进行拼接
// url = process.env.VUE_APP_BASE_API + url;
// insertFn(url, '', res.fileName); //插入图片
// })
// }
}
}
}
// elementui配置
// 日期选择配置
import QRCode from "qrcodejs2";
export const pickerOptions = {
shortcuts: [{
text: '未来一周',
onClick(picker) {
let start = getNowDate_01();
let end = getNowDate_01();
end.setTime(end.getTime() + 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '未来一个月',
onClick(picker) {
let start = getNowDate_01();
let end = getNowDate_01();
end.setTime(end.getTime() + 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '未来三个月',
onClick(picker) {
let start = getNowDate_01();
let end = getNowDate_01();
end.setTime(end.getTime() + 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}, {
text: '未来半年',
onClick(picker) {
let start = getNowDate_01();
let end = getNowDate_01();
end.setTime(end.getTime() + 3600 * 1000 * 24 * 180);
picker.$emit('pick', [start, end]);
}
}, {
text: '未来一年',
onClick(picker) {
let start = getNowDate_01();
let end = getNowDate_01();
end.setTime(end.getTime() + 3600 * 1000 * 24 * 365);
picker.$emit('pick', [start, end]);
}
}, {
text: '未来两年',
onClick(picker) {
let start = getNowDate_01();
let end = getNowDate_01();
end.setTime(end.getTime() + 3600 * 1000 * 24 * 365 * 2);
picker.$emit('pick', [start, end]);
}
}, {
text: '未来五年',
onClick(picker) {
let start = getNowDate_01();
let end = getNowDate_01();
end.setTime(end.getTime() + 3600 * 1000 * 24 * 365 * 5);
picker.$emit('pick', [start, end]);
}
}],
//禁用当前日期之前的日期
// disabledDate(time) {
// //Date.now()是javascript中的内置函数,它返回自1970年1月1日00:00:00 UTC以来经过的毫秒数。
// return time.getTime() < Date.now() - 8.64e7;
// }
}
// 打开网页预览
export function openIframe(url, show, urlItem) {
// 判断是否需要打开新的ifrema还是在本页面打开
if ((url.indexOf("app.urumqimtr.com") > -1) || (url.indexOf("localhost:") > -1)) {
show.html = true;
urlItem.htmlUrl = url;
} else {
window.open(url);
}
}
// 获取图片地址,有的图片是网络图片。有的图片是本地图片,尽量都能展示
export function imgUrlDeal(url) {
if (url) {
// 以/为开头说明是本地服务器图片
if (url.startsWith("/")) {
return process.env.VUE_APP_BASE_API + url
}
return url;
} else {
return null;
}
}
// 格式化日年月日
export function getNowDate_01() {
let date = new Date();
let year = date.getFullYear() // 年
let month = date.getMonth() + 1; // 月
let day = date.getDate(); // 日
// 给一位数的数据前面加 “0”
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (day >= 0 && day <= 9) {
day = "0" + day;
}
let time = year + "-" + month + "-" + day;
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm),'');
date = new Date(time);
return date;
}
// 格式化日对象
export function getNowDate() {
let date = new Date();
let sign2 = ":";
let year = date.getFullYear() // 年
let month = date.getMonth() + 1; // 月
let day = date.getDate(); // 日
let hour = date.getHours(); // 时
let minutes = date.getMinutes(); // 分
let seconds = date.getSeconds() //秒
const weekArr = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期天'];
const week = weekArr[date.getDay()];
// 给一位数的数据前面加 “0”
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (day >= 0 && day <= 9) {
day = "0" + day;
}
if (hour >= 0 && hour <= 9) {
hour = "0" + hour;
}
if (minutes >= 0 && minutes <= 9) {
minutes = "0" + minutes;
}
if (seconds >= 0 && seconds <= 9) {
seconds = "0" + seconds;
}
return year + "-" + month + "-" + day + " " + hour + sign2 + minutes + sign2 + seconds;
}
先创建模板文件
${title}
<#-- -->
<#-- -->
<#--
-->
<#-- 2022-07-22
-->
<#-- 富文本中的内容 -->
${content}
# 项目相关配置
myWeb:
# 名称
name: myWeb
# 版本
version: 0.0.1
# 版权年份
copyrightYear: 2021
# 实例演示开关
demoEnabled: false
# 文件路径 示例( Windows配置D:/myWeb/uploadPath,Linux配置 /home/myWeb/uploadPath)
profile: ./uploadfile
# profile: D:/home/file
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数组计算 char 字符验证
captchaType: char
# 共享盘映射地址,示例( Windows配置D:/myWeb/uploadPath,Linux配置 /home/myWeb/uploadPath)
# sharedDisk: /HceShare/hceapp
sharedDisk: D:/HceShare/hceapp
# 上传的文件大小 140MB
fileMaxSize: 146800640
# 静态资源对应的服务器域名或地址
# 生产地址
#serverPrefix: https://xxxxxxx:9969/RecordFile
# 测试环境地址
#serverPrefix: http://xxxxxxx:8043/ggpt
# 本地
serverPrefix: http://localhost:8043/ggpt
@Value("${serverPrefix}")
public String serverPrefix;
@RepeatSubmit(interval = 3500)
public AjaxResult add(OpeNewsInfo opeNewsInfo) throws IOException, TemplateException, ParseException {
LoginUser loginUser = getLoginUser();
// 上传到共享盘,成功后会返回路径,调用的是otherAddressesUpload()
String picaddr = FileUploadUtils.upload(myWeb.getSharedDiskImgPash(), myWeb.getSharedDisk(), opeNewsInfo.getPicNmFile());
// 如果是本地页面才会生成页面
if ("0".equals(opeNewsInfo.getPageType()) ) {
// 获取内容
String htmlAddress = FreemarkBeanUtil.getHtmlAddress(opeNewsInfo.getNewsTitle(), opeNewsInfo.getNewsContent(), loginUser);
opeNewsInfo.setNewsLinkAddress(StringUtils.isNotEmpty(htmlAddress) ? serverPrefix + htmlAddress : "");
}
插入数据库。。。。。
}
package com.goldsign.common.utils.file;
import java.io.*;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.URLUtil;
import com.goldsign.common.utils.ServletUtils;
import com.goldsign.common.utils.VideoUtil;
import com.goldsign.common.utils.ip.IpUtils;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.FilenameUtils;
import org.apache.http.entity.ContentType;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.goldsign.common.config.GoldSignConfig;
import com.goldsign.common.constant.Constants;
import com.goldsign.common.exception.file.FileNameLengthLimitExceededException;
import com.goldsign.common.exception.file.FileSizeLimitExceededException;
import com.goldsign.common.exception.file.InvalidExtensionException;
import com.goldsign.common.utils.DateUtils;
import com.goldsign.common.utils.StringUtils;
import com.goldsign.common.utils.uuid.IdUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
/**
* 文件上传工具类
*
* @author ruoyi
*/
@Component
public class FileUploadUtils {
protected static final Logger log = LoggerFactory.getLogger(FileUploadUtils.class);
@Autowired
static IpUtils ipUtils;
@Value("${serverPrefix}")
public String serverPrefix;
public static String serve_prefix;
//利用@PostConstruct将yml中配置的值赋给本地的变量
@PostConstruct
public void getEnvironment() {
this.serve_prefix = this.serverPrefix;
}
/**
* 默认的文件名最大长度 100
*/
public static final int DEFAULT_FILE_NAME_LENGTH = 350;
/**
* 默认上传的地址
*/
private static String defaultBaseDir = GoldSignConfig.getProfile();
public static void setDefaultBaseDir(String defaultBaseDir) {
FileUploadUtils.defaultBaseDir = defaultBaseDir;
}
public static String getDefaultBaseDir() {
return defaultBaseDir;
}
/**
* 以默认配置进行文件上传
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
public static final String upload(MultipartFile file) throws IOException {
try {
return upload(getDefaultBaseDir(), null, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
/**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param fileHttpMapping 文件所存在的映射根目录,该目录被资源拦截器映射成了本服务的的静态资源
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
public static final String upload(String baseDir, String fileHttpMapping, MultipartFile file) throws IOException {
try {
return upload(baseDir, fileHttpMapping, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
/**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param fileHttpMapping 文件所存在的映射根目录,该目录被资源拦截器映射成了本服务的的静态资源
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
public static final String upload(String baseDir, String fileHttpMapping, MultipartFile file, String[] allowedExtension) throws Exception {
int fileNamelength = file.getOriginalFilename().length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
File desc = getAbsoluteFile(baseDir, fileName);
file.transferTo(desc);
String pathFileName = getPathFileName(baseDir, fileHttpMapping, fileName);
return pathFileName;
}
/**
* 编码文件名
*/
public static final String extractFilename(MultipartFile file) {
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
return fileName;
}
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.exists()) {
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
}
return desc;
}
public static final String getPathFileName(String uploadDir, String fileHttpMapping, String fileName) throws IOException {
if (fileHttpMapping == null) {
fileHttpMapping = GoldSignConfig.getProfile();
}
String pathFileName;
int dirLastIndex = fileHttpMapping.length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
// 如果是本服務對應的文件夾,那麽就加上指定前綴,可以让该请求访问本服务时,映射到指定的静态资源上
if (fileHttpMapping.equals(GoldSignConfig.getProfile())) {
// Constants.RESOURCE_PREFIX 是本服务默认的开头前缀
pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
} else {
// 这个就不用加前缀, 因为其他服务(hce后管)也要访问,所以我们文件路径的格式不能和框架指定的格式保持一致,要跟hce后管的文件系统保持一致,而且上传的时候我们也指定了前缀,如果要加新的文件类型,则在配置文件中增加配置,同时在ResourcesConfig中增加对该目录的映射处理
pathFileName = "/" + currentDir + "/" + fileName;
}
return pathFileName;
}
/**
* 文件大小校验
*
* @param file 上传的文件
* @return
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws InvalidExtensionException
*/
public static final void assertAllowed(MultipartFile file, String[] allowedExtension) throws FileSizeLimitExceededException, InvalidExtensionException {
long size = file.getSize();
long fileMaxSize = GoldSignConfig.getFileMaxSize();
if (fileMaxSize != -1 && size > fileMaxSize) {
throw new FileSizeLimitExceededException(fileMaxSize / 1024 / 1024);
}
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
fileName);
} else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
fileName);
} else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
fileName);
} else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
fileName);
} else {
throw new InvalidExtensionException(allowedExtension, extension, fileName);
}
}
}
/**
* 判断MIME类型是否是允许的MIME类型
*
* @param extension
* @param allowedExtension
* @return
*/
public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
for (String str : allowedExtension) {
if (str.equalsIgnoreCase(extension)) {
return true;
}
}
return false;
}
// 根据网页内容生成h5文件
public static String getH5Path(String h5Path, String content) throws IOException {
InputStream inputStream = null;
try {
inputStream = new ByteArrayInputStream(content.getBytes());
MultipartFile vFile = new MockMultipartFile("xx.html", "xx.html", ContentType.TEXT_HTML.toString(), inputStream);
// 保存新闻,这里获取的是h5
h5Path = upload(GoldSignConfig.getSharedDiskH5Pash(), GoldSignConfig.getSharedDisk(), vFile);
} catch (IOException e) {
e.printStackTrace();
log.error("网页生成上传失败,网页URL:{},异常:{}", h5Path, e.getMessage());
} finally {
if (inputStream != null) {
inputStream.close();
}
}
return h5Path;
}
/**
* 获取文件名的后缀
*
* @param file 表单文件
* @return 后缀名
*/
public static final String getExtension(MultipartFile file) {
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (StringUtils.isEmpty(extension)) {
extension = MimeTypeUtils.getExtension(file.getContentType());
}
return extension;
}
/**
* 将相对服务的相对路径,修改为相对为本文件的路径,避免服务地址修改的时候,或者服务名修改的时候产生不必要的维护
*
* @param els
*/
public static void extracted(Elements els) throws IOException {
String src;
for (Element el : els) {
if ("video".equals(el.tag().getName())) {
Elements source = el.select("source[src]");
src = source.attr("src");
if (StringUtils.isNotBlank(src)) {
// 第一步,判断是否是绝对路径
if (!isAbsolutePath(src)) {
// 相对路径设置
el.attr("src", getHtmlTagRelativeUrl(src));
}
// 第二步,先不修改src的地址为相对地址,先获取视频封面字节数据
byte[] bytesImg = VideoUtil.getScreenshot(src);
if (bytesImg != null) {
MultipartFile vFile = null;
InputStream inputStream = null;
try {
// 构造inputStream
inputStream = new ByteArrayInputStream(bytesImg);
// 将inputStream转成MultipartFile
vFile = new MockMultipartFile("xx.jpeg", "xx.jpeg", ContentType.IMAGE_JPEG.toString(), inputStream);
// 上传封面。获取上传后的地址
String h5Path = upload(GoldSignConfig.getSharedDiskCVideoCoverPath(), GoldSignConfig.getSharedDisk(), vFile);
el.attr("poster", getHtmlTagRelativeUrl(h5Path));
} catch (IOException e) {
e.printStackTrace();
log.error("视频封面转换失败,视频URL:{},异常:{}", src, e.getMessage());
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}
} else {
src = el.attr("src");
el.attr("src", getHtmlTagRelativeUrl(src));
String style = el.attr("style");
el.attr("style", "max-width: 100%;height: auto");
}
}
}
/**
* 为标签设置图片,修改相对路径
*
* @param src
*/
private static String getHtmlTagRelativeUrl(String src) {
// 判断如果有 配置文件中的serverPrefix地址,那么就替换成空字符串,这样在生成的文件中保存的就是相对地址了,相对地址不依赖域名,更灵活
src = src.replace(serve_prefix, "");
// 如果是以userfile开头的就替换
if (src.startsWith("/userfiles/ggpt/")) {
// 这里替换成相对路径,避免不必要的拼接数据,造成元数据污染
return src.replace("/userfiles/ggpt/", "../../../../");
}
return src;
}
/**
* 将字符串通过outputStream输入到file中
* 这个方法有问题,会在本地生成文件,所以放弃使用了
*
* @param title
* @param htmlText
* @return
* @throws IOException
*/
public static CommonsMultipartFile getCommonsMultipartFile(String title, String htmlText, File file) throws IOException {
// file为空就使用htmlcontent,否则就使用传递进来的file
if (file == null) {
file = new File(title + ".html");
OutputStream fos = new FileOutputStream(file);
fos.write(htmlText.getBytes(StandardCharsets.UTF_8));
try {
fos.flush();
fos.close();
} catch (IOException e) {
}
}
DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem("file", MediaType.ALL_VALUE, true, file.getName());
try (InputStream input = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()) {
IOUtils.copy(input, os);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid file: " + e, e);
}
CommonsMultipartFile multipartFile = new CommonsMultipartFile(fileItem);
return multipartFile;
}
/**
* 判断是否绝对路径
* 当路径以 / 开头则为相对路径,否则视为绝对路径
*
* @param uploadDir
* @return
*/
private static boolean isAbsolutePath(String uploadDir) {
if (uploadDir.startsWith("/")) {
return false;
}
return true;
}
/**
* 获取本机地址
*
* @return
*/
public static String getUrl() {
return getDomain(ServletUtils.getRequest());
}
public static String getDomain(HttpServletRequest request) {
StringBuffer url = request.getRequestURL();
String contextPath = request.getServletContext().getContextPath();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
}
}
package com.goldsign.common.utils;
import cn.hutool.core.img.ImgUtil;
import com.goldsign.common.exception.ServiceException;
import com.goldsign.common.utils.ip.IpUtils;
import com.goldsign.common.utils.uuid.IdUtils;
//import org.bytedeco.javacpp.opencv_core;
//import org.bytedeco.javacv.FFmpegFrameGrabber;
//import org.bytedeco.javacv.Frame;
//import org.bytedeco.javacv.Java2DFrameConverter;
//import java.awt.*;
//import java.awt.image.BufferedImage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
//import java.io.File;
//
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacv.*;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
/**
* @author:yuchen
* @createTime:2022/7/11 16:07
*/
@Component
public class VideoUtil {
protected static final Logger log = LoggerFactory.getLogger(VideoUtil.class);
public static void main(String[] args) {
// byte[] screenshot = getScreenshot("C:\\Users\\86181\\Downloads\\下载 (1).mp4");
// System.out.println(file.getName());
// getVideoPic();
}
@Autowired
public IpUtils ipUtils;
@Autowired
public static IpUtils _ipUtils;
@Value("${serverPrefix}")
public String serverPrefix;
public static String serve_prefix;
//利用@PostConstruct将yml中配置的值赋给本地的变量
@PostConstruct
public void getEnvironment(){
this.serve_prefix = this.serverPrefix;
this._ipUtils = this.ipUtils;
}
// public static String getVideoPic() {
// String url = "https://xxx/xxxx.mp4";
// String value = "1111123";
// String dir = value +"/";
// String fileName = FileUtil.getName(url);// 100.mp4
// String targetFileName = "thumb_"+fileName.substring(0, fileName.lastIndexOf("."))+".jpg";
// File filePath = new File(PATH+dir);
// if (!filePath.exists()) {
// boolean mkdirs = filePath.mkdirs();
// if(mkdirs){
// log.info("文件夹创建成功:{}",filePath.getAbsolutePath());
// }
// }
// String outPath = PATH+dir+targetFileName;
// File outFile = getScreenshot(url, outPath);
// if(outFile != null){
// String fileName1 = AwsFileUtil.uploadS3Us(outFile, imei, targetFileName);
// System.out.println(fileName1);
// boolean delete = outFile.delete();
// if(delete){
// log.info("删除本地临时文件成功");
// }
// return fileName1;
// }
// return "";
// }
/**
* 获取视频封面图
*
* @param filePath 视频地址
* @return map
*/
// public static byte[] getScreenshot(String filePath) {
// log.info("截取视频截图开始:Video={}", filePath);
// filePath = filePath.replaceAll("\\\\", "/");
// File file = null;
// FFmpegFrameGrabber grabber = null;
// try {
// grabber = FFmpegFrameGrabber.createDefault(filePath);
// grabber.setOption("stimeout", "2000000");
// grabber.start();
// //设置视频截取帧(建议从5帧开始,防止全是黑屏)
// Frame frame = null;
// for (int j = 0; j < 5; j++) {
// frame = grabber.grabImage();
// }
// //视频旋转度
// String rotate = grabber.getVideoMetadata("rotate");
// Java2DFrameConverter converter = new Java2DFrameConverter();
// //绘制图片
// BufferedImage image = converter.getBufferedImage(frame);
// if (rotate != null) {
// // 旋转图片
// image = rotate(image, Integer.parseInt(rotate));
// }
// //创建文件
file = new File(IdUtils.randomUUID() + ".jpg");
ImgUtil.write(image, file);
//
// ByteArrayOutputStream out = new ByteArrayOutputStream();
// boolean flag = ImageIO.write(image, "gif", out);
//
// byte[] b = out.toByteArray();
// out.write(b);
// log.info("截取视频截图成功");
// return b;
// } catch (Exception e) {
// throw new ServiceException("获取视频封面图,Error,请检查视频链接是否正常");
// } finally {
// if (grabber != null) {
// try {
// grabber.stop();
// } catch (Exception ignored) {
// }
// }
// }
// }
public static byte[] getScreenshot(String filePath) {
// 说明是外部资源,需要拼接上https
if (filePath.startsWith("//") && !filePath.startsWith("/userfiles/ggpt")){
filePath = "https:" + filePath;
}
// 有些连文件地址不太正常,且没有后缀,不要紧; 文档资料参考链接:【https://www.codenong.com/7273573/】
// 如果发现是以yml配置开头的url路径,那么就替换成localhost路径
filePath = filePath.replaceFirst(serve_prefix,_ipUtils.getAccessPath());
byte[] bytes = null;
FFmpegFrameGrabber ff = null;
try {
ff = FFmpegFrameGrabber.createDefault(filePath);
ff.start();
String rotate = ff.getVideoMetadata("rotate");
Frame f;
int i = 0;
while (i < 6) {
f = ff.grabImage();
opencv_core.IplImage src = null;
if (null != rotate && rotate.length() > 1) {
OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
src = converter.convert(f);
f = converter.convert(rotate(src, Integer.valueOf(rotate)));
}
i++;
if (i == 6) {
bytes = doExecuteFrame(f);
}
}
ff.stop();
} catch (FrameGrabber.Exception e) {
log.error("视频截取失败");
// throw new ServiceException("获取视频封面图,Error,请检查视频链接是否正常");
} finally {
if (ff != null) {
try {
ff.close();
} catch (Exception e) {
}
}
}
return bytes;
}
/*
* 旋转角度的
*/
public static opencv_core.IplImage rotate(opencv_core.IplImage src, int angle) {
opencv_core.IplImage img = opencv_core.IplImage.create(src.height(), src.width(), src.depth(), src.nChannels());
opencv_core.cvTranspose(src, img);
opencv_core.cvFlip(img, img, angle);
return img;
}
public static byte[] doExecuteFrame(Frame f) {
if (null == f || null == f.image) {
return new byte[0];
}
Java2DFrameConverter converter = new Java2DFrameConverter();
String imageMat = "jpg";
BufferedImage bi = converter.getBufferedImage(f);
System.out.println("width:" + bi.getWidth());
System.out.println("height:" + bi.getHeight());
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
boolean flag = ImageIO.write(bi, "jpg", out);
byte[] b = out.toByteArray();
log.info("截取视频截图成功");
return b;
} catch (IOException e) {
log.error("封面截取失败");
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
return null;
}
/**
* @param src ""
* @param angel 视频旋转度
* @return BufferedImage
* @Description 根据视频旋转度来调整图片
*/
private static BufferedImage rotate(BufferedImage src, int angel) {
int src_width = src.getWidth(null);
int src_height = src.getHeight(null);
int type = src.getColorModel().getTransparency();
Rectangle rect_des = calcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);
BufferedImage bi = new BufferedImage(rect_des.width, rect_des.height, type);
Graphics2D g2 = bi.createGraphics();
g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2);
g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);
g2.drawImage(src, 0, 0, null);
g2.dispose();
return bi;
}
/**
* @param src ""
* @param angel ""
* @return Rectangle
* @Description: 计算图片旋转大小
*/
private static Rectangle calcRotatedSize(Rectangle src, int angel) {
if (angel >= 90) {
if (angel / 90 % 2 == 1) {
int temp = src.height;
src.height = src.width;
src.width = temp;
}
angel = angel % 90;
}
double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
double angel_dalta_width = Math.atan((double) src.height / src.width);
double angel_dalta_height = Math.atan((double) src.width / src.height);
int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_width));
int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_height));
int des_width = src.width + len_dalta_width * 2;
int des_height = src.height + len_dalta_height * 2;
return new java.awt.Rectangle(new Dimension(des_width, des_height));
}
}
package com.goldsign.common.config;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Date;
/**
* 读取项目相关配置
*
* @author ruoyi
*/
@Component
@ConfigurationProperties(prefix = "myWeb")
public class myWebSignConfig {
/**GoldSignConfig
* 项目名称
*/
private String name;
/**
* 版本
*/
private String version;
/**
* 版权年份
*/
private String copyrightYear;
/**
* 实例演示开关
*/
private boolean demoEnabled;
/**
* 上传最大文件大小配置
*/
private static long fileMaxSize;
/**
* 上传路径
*/
private static String profile;
/* 共享盘地址*/
private static String sharedDisk;
/**
* 获取地址开关
*/
private static boolean addressEnabled;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getCopyrightYear() {
return copyrightYear;
}
public void setCopyrightYear(String copyrightYear) {
this.copyrightYear = copyrightYear;
}
public boolean isDemoEnabled() {
return demoEnabled;
}
public void setDemoEnabled(boolean demoEnabled) {
this.demoEnabled = demoEnabled;
}
public static String getProfile() {
return profile;
}
public void setProfile(String profile) {
GoldSignConfig.profile = profile;
}
public static boolean isAddressEnabled() {
return addressEnabled;
}
public void setAddressEnabled(boolean addressEnabled) {
GoldSignConfig.addressEnabled = addressEnabled;
}
public static String getSharedDisk() {
return sharedDisk;
}
public void setSharedDisk(String sharedDisk) {
GoldSignConfig.sharedDisk = sharedDisk;
}
public static long getFileMaxSize() {
return fileMaxSize;
}
public void setFileMaxSize(long fileMaxSize) {
GoldSignConfig.fileMaxSize = fileMaxSize;
}
/**
* 获取导入上传路径
*/
public static String getImportPath() {
return getProfile() + "/import";
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath() {
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath() {
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath() {
return getProfile() + "/upload";
}
/**
* 获取共享盘图片上传路径
*
* @return
*/
public static String getSharedDiskImgPash() {
// 如果上传的目录不1位则补0
String path = getSharedDisk() + "/userfiles/ggpt/image";
File file = new File(path);
if (file.exists()) {
file.mkdirs();
}
return path;
}
/**
* 获取共享盘【视频文件】上传路径
*
* @return
*/
public static String getSharedDiskVideoPash() {
// 如果上传的目录不1位则补0
String path = getSharedDisk() + "/userfiles/ggpt/video";
File file = new File(path);
if (file.exists()) {
file.mkdirs();
}
return path;
}
/**
* 获取共享盘网页上传路径
*
* @return
*/
public static String getSharedDiskH5Pash() {
// 如果上传的目录不1位则补0
String path = getSharedDisk() + "/userfiles/ggpt/h5";
File file = new File(path);
if (file.exists()) {
file.mkdirs();
}
return path;
}
/**
* 获取共享盘图片上传路径
*
* @return
*/
public static String getSharedDiskCVideoCoverPath() {
// 如果上传的目录不1位则补0
String path = getSharedDisk() + "/userfiles/ggpt/videoCover";
File file = new File(path);
if (file.exists()) {
file.mkdirs();
}
return path;
}
}
org.jsoup
jsoup
1.8.3
视频处理
org.bytedeco
javacv-platform
1.3.1
org.springframework
spring-test
cn.hutool
hutool-all
5.7.13
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/** 本地文件上传路径 */
// 后管服务自带的目录
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
// 映射到指定的目錄
.addResourceLocations("file:" + GoldSignConfig.getProfile() + "/");
// 这里是hce后管的文件目录,如果是userfiles路径,那么映射到D:/HceShare/hceapp + “/userfiles/” 目录下面
registry.addResourceHandler("/userfiles" + "/**")
// 映射到指定的目錄
.addResourceLocations("file:" + GoldSignConfig.getSharedDisk() + "/userfiles/");
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
}
package com.goldsign.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.goldsign.common.annotation.Excel;
import lombok.experimental.FieldDefaults;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.web.multipart.MultipartFile;
import java.util.Date;
/**
* 广告轮播图对象 OPE_AD_PIC_INFO
*
* @author goldsign
* @date 2022-07-08
*/
@KeySequence(value = "SEQ_OPE_AD_PIC_INFO", clazz = String.class)
public class OpeAdPicInfo extends Model {
private static final long serialVersionUID = 1L;
/** 上传的图片文件 */
private MultipartFile picNmFile;
/** $column.columnComment */
@TableId(value = "id", type = IdType.INPUT)
private String id;
/** 图片名称 */
@Excel(name = "图片名称")
private String picNm;
/** 图片地址 */
@Excel(name = "图片地址")
private String picAddr;
/** 图片描叙 */
@Excel(name = "图片描叙")
private String picDesc;
/** 图片是否显示 */
@Excel(name = "图片是否显示")
private String picShow;
/** 创建人 */
@Excel(name = "创建人")
private String creater;
/** 创建时间 */
@Excel(name = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createDt;
/** 最新修改人 */
@Excel(name = "最新修改人")
private String modifier;
/** 最新修改时间 */
@Excel(name = "最新修改时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date modifyDt;
/** 备注 */
@Excel(name = "备注")
private String remarks;
/** 删除标记 */
private String deleteFlag;
/** 图片超链接 */
@Excel(name = "图片超链接")
private String picHyperlink;
/** 广告类型 */
@Excel(name = "广告类型")
private String adType;
/** 广告类型 */
@Excel(name = "广告类型")
private String linkType;
/** 倒计时(秒) */
@Excel(name = "倒计时", readConverterExp = "秒")
private Integer countdown;
/** $column.columnComment */
@Excel(name = "倒计时", readConverterExp = "$column.readConverterExp()")
private String savepic;
@Excel(name = "发布时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date releaseTime;
@Excel(name = "锁定时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date lockTime;
/** 富文本 */
private String richText;
// 网页类型,0本地页面,1第三方页面
private String pageType;
public String getPageType() {
return pageType;
}
public void setPageType(String pageType) {
this.pageType = pageType;
}
public Date getReleaseTime() {
return releaseTime;
}
public void setReleaseTime(Date releaseTime) {
this.releaseTime = releaseTime;
}
public Date getLockTime() {
return lockTime;
}
public void setLockTime(Date lockTime) {
this.lockTime = lockTime;
}
public String getRichText() {
return richText;
}
public void setRichText(String richText) {
this.richText = richText;
}
public void setId(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
public void setPicNm(String picNm)
{
this.picNm = picNm;
}
public String getPicNm()
{
return picNm;
}
public void setPicAddr(String picAddr)
{
this.picAddr = picAddr;
}
public String getPicAddr()
{
return picAddr;
}
public void setPicDesc(String picDesc)
{
this.picDesc = picDesc;
}
public String getPicDesc()
{
return picDesc;
}
public void setPicShow(String picShow)
{
this.picShow = picShow;
}
public String getPicShow()
{
return picShow;
}
public void setCreater(String creater)
{
this.creater = creater;
}
public String getCreater() {
return creater;
}
public Date getCreateDt() {
return createDt;
}
public void setCreateDt(Date createDt) {
this.createDt = createDt;
}
public String getModifier() {
return modifier;
}
public void setModifier(String modifier) {
this.modifier = modifier;
}
public Date getModifyDt() {
return modifyDt;
}
public void setModifyDt(Date modifyDt) {
this.modifyDt = modifyDt;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
public String getRemarks()
{
return remarks;
}
public void setDeleteFlag(String deleteFlag)
{
this.deleteFlag = deleteFlag;
}
public String getDeleteFlag()
{
return deleteFlag;
}
public void setPicHyperlink(String picHyperlink)
{
this.picHyperlink = picHyperlink;
}
public String getPicHyperlink()
{
return picHyperlink;
}
public void setAdType(String adType)
{
this.adType = adType;
}
public String getAdType()
{
return adType;
}
public void setLinkType(String linkType)
{
this.linkType = linkType;
}
public String getLinkType()
{
return linkType;
}
public void setCountdown(Integer countdown)
{
this.countdown = countdown;
}
public Integer getCountdown()
{
return countdown;
}
public void setSavepic(String savepic)
{
this.savepic = savepic;
}
public String getSavepic()
{
return savepic;
}
public MultipartFile getPicNmFile() {
return picNmFile;
}
public void setPicNmFile(MultipartFile picNmFile) {
this.picNmFile = picNmFile;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("picNm", getPicNm())
.append("picAddr", getPicAddr())
.append("picDesc", getPicDesc())
.append("picShow", getPicShow())
.append("creater", getCreater())
.append("createDt", getCreateDt())
.append("modifier", getModifier())
.append("modifyDt", getModifyDt())
.append("remarks", getRemarks())
.append("deleteFlag", getDeleteFlag())
.append("picHyperlink", getPicHyperlink())
.append("adType", getAdType())
.append("linkType", getLinkType())
.append("countdown", getCountdown())
.append("savepic", getSavepic())
.toString();
}
}
在sys_menu中有路由参数配置,请参考ruoyi官网配置,vue组件中有用到
需要用到ipUtils这个在若依里面有,就是用来获取当前服务的地址和端口号+服务名的
最后还需要修改若依框架的上传代码中文件路径的参数设置