|
|
package com.ruoyi.system.service;
|
|
|
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
import cn.hutool.core.img.Img;
|
|
|
import cn.hutool.core.lang.UUID;
|
|
|
import cn.hutool.core.util.HexUtil;
|
|
|
import cn.hutool.crypto.SecureUtil;
|
|
|
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
|
|
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
|
|
import com.ruoyi.common.core.domain.PageQuery;
|
|
|
import com.ruoyi.common.core.page.TableDataInfo;
|
|
|
import com.ruoyi.common.helper.DataPermissionHelper;
|
|
|
import com.ruoyi.common.utils.IdUtils;
|
|
|
import com.ruoyi.system.domain.SysOss;
|
|
|
import com.ruoyi.system.domain.bo.SysOssBo;
|
|
|
import com.ruoyi.system.domain.vo.SysOssVo;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import java.awt.*;
|
|
|
import java.awt.image.BufferedImage;
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
import java.io.IOException;
|
|
|
import java.io.InputStream;
|
|
|
import java.nio.ByteBuffer;
|
|
|
import java.util.Calendar;
|
|
|
import java.util.Collection;
|
|
|
import java.util.Date;
|
|
|
import java.util.List;
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
|
/**
|
|
|
* 文件上传 服务层
|
|
|
*
|
|
|
* @author Lion Li
|
|
|
*/
|
|
|
public interface ISysOssService {
|
|
|
|
|
|
/**
|
|
|
* 服务商
|
|
|
*/
|
|
|
public static enum Service {
|
|
|
minio, qiniu, aliyun, qcloud, image, upload;
|
|
|
}
|
|
|
|
|
|
String IMAGE_WEBP = "webp";
|
|
|
|
|
|
/**
|
|
|
* 默认路径前缀
|
|
|
*/
|
|
|
String PRE_DEFAULT = "default";
|
|
|
|
|
|
/**
|
|
|
* 设置指定服务商
|
|
|
*
|
|
|
* @param handle
|
|
|
*/
|
|
|
void setService(Service service, Runnable handle);
|
|
|
|
|
|
public <T> T setService(Service service, Supplier<T> handle);
|
|
|
|
|
|
/**
|
|
|
* 只保存,不存放记录
|
|
|
*
|
|
|
* @param handle 处理执行方法
|
|
|
*/
|
|
|
void ignore(Runnable handle);
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 只保存,不存放记录
|
|
|
*
|
|
|
* @param handle 处理执行方法
|
|
|
*/
|
|
|
public <T> T ignore(Supplier<T> handle);
|
|
|
|
|
|
|
|
|
TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery);
|
|
|
|
|
|
List<SysOssVo> listByIds(Collection<Long> ossIds);
|
|
|
|
|
|
SysOssVo getById(Long ossId);
|
|
|
|
|
|
default SysOssVo upload(MultipartFile file) {
|
|
|
return upload(file, PRE_DEFAULT);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @param file
|
|
|
* @param pre - 图片保存路径前缀
|
|
|
* @return
|
|
|
*/
|
|
|
default SysOssVo upload(MultipartFile file, String pre) {
|
|
|
if (file == null || file.isEmpty()) {
|
|
|
throw new RuntimeException("文件不能为空");
|
|
|
}
|
|
|
try {
|
|
|
return save(file.getInputStream(), file.getOriginalFilename(), file.getContentType(), pre);
|
|
|
} catch (IOException e) {
|
|
|
throw new RuntimeException(e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 保存文件, rule:{yyyy}/{MM}/{dd}/{id36}.{ext}
|
|
|
*
|
|
|
* @param in 输入流
|
|
|
* @param filename 文件名
|
|
|
* @param contentType 文件类型
|
|
|
* @param pre 路径前缀
|
|
|
* @return
|
|
|
*/
|
|
|
default SysOssVo save(InputStream in, String filename, String contentType, String pre) {
|
|
|
return save(in, filename, contentType, pre, "{yyyy}/{MM}/{dd}/{id36}.{ext}");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 保存文件,保留文件名,rule:{yyyy}/{MM}/{dd}/{id36}/{filename}.{ext}
|
|
|
*
|
|
|
* @param in 输入流
|
|
|
* @param filename 文件名
|
|
|
* @param contentType 文件类型
|
|
|
* @param pre 路径前缀
|
|
|
* @return
|
|
|
*/
|
|
|
default SysOssVo saveFilename(InputStream in, String filename, String contentType, String pre) {
|
|
|
return save(in, filename, contentType, pre, "{yyyy}/{MM}/{dd}/{id36}/{filename}.{ext}");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 保存文件,存放路径规则rule:
|
|
|
* <pre>
|
|
|
* - 根据存放规则和文件名生成存放的URI
|
|
|
* - 支持
|
|
|
* - {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
|
|
|
* - {UUID} 32位的唯一标志
|
|
|
* - {i} 自增id
|
|
|
* - {id} 当日int类型的唯一id,防止重复建议+年月日路径
|
|
|
* - {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
|
|
|
* - {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
|
|
|
* - {filename} 文件基础名称
|
|
|
* - {ext} 扩展名
|
|
|
* </pre>
|
|
|
*
|
|
|
* @param in 输入流
|
|
|
* @param filename 文件名
|
|
|
* @param contentType 文件类型
|
|
|
* @param pre 路径前缀
|
|
|
* @param rule 路径规则
|
|
|
* @return
|
|
|
*/
|
|
|
SysOssVo save(InputStream in, String filename, String contentType, String pre, String rule);
|
|
|
|
|
|
SysOssVo uploadImgs(MultipartFile file, String pre);
|
|
|
|
|
|
default SysOssVo uploadImgs(MultipartFile file, String pre, int maxWidth, int maxHeight, BufferedImage watermark) {
|
|
|
if (file == null || file.isEmpty()) {
|
|
|
throw new RuntimeException("图片不能为空");
|
|
|
}
|
|
|
try {
|
|
|
return uploadImgs(file.getInputStream(), pre, maxWidth, maxHeight, watermark);
|
|
|
} catch (IOException e) {
|
|
|
throw new RuntimeException(e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @param in
|
|
|
* @param pre - 图片保存路径前缀
|
|
|
* @param maxWidth - 缩放到指定的宽 小于1表示不缩放
|
|
|
* @param maxHeight - 缩放到指定的高 小于1表示不缩放
|
|
|
* @param watermark - 水印图片 null表示不加水印
|
|
|
* @return
|
|
|
*/
|
|
|
SysOssVo uploadImgs(InputStream in, String pre, int maxWidth, int maxHeight,
|
|
|
BufferedImage watermark);
|
|
|
|
|
|
void download(Long ossId, HttpServletResponse response) throws IOException;
|
|
|
|
|
|
void download(String url, Service service, HttpServletResponse response) throws IOException;
|
|
|
|
|
|
InputStream download(Long ossId) throws IOException;
|
|
|
|
|
|
InputStream download(SysOssVo sysOss) throws IOException;
|
|
|
|
|
|
InputStream download(String url, Service service) throws IOException;
|
|
|
|
|
|
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
|
|
|
|
|
|
|
|
default InputStream formatImage(MultipartFile file, int maxWidth, int maxHeight,
|
|
|
BufferedImage watermark) {
|
|
|
try {
|
|
|
if (file == null || file.isEmpty()) {
|
|
|
throw new RuntimeException("上传图片不能为空");
|
|
|
}
|
|
|
if (!file.getContentType().startsWith("image/")) {
|
|
|
throw new RuntimeException("上传的文件不是图片");
|
|
|
}
|
|
|
return formatImage(file.getInputStream(), maxWidth, maxHeight, watermark);
|
|
|
} catch (IOException e) {
|
|
|
throw new RuntimeException("处理图片错误", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
default InputStream formatImage(Image img, int maxWidth, int maxHeight, BufferedImage watermark) {
|
|
|
if (img == null) {
|
|
|
throw new RuntimeException("图片不能为空");
|
|
|
}
|
|
|
return formatImage(Img.from(img), maxWidth, maxHeight, watermark);
|
|
|
}
|
|
|
|
|
|
default InputStream formatImage(InputStream in, int maxWidth, int maxHeight, BufferedImage watermark) {
|
|
|
return formatImage(Img.from(in), maxWidth, maxHeight, watermark);
|
|
|
}
|
|
|
|
|
|
default InputStream formatImage(Img img, int maxWidth, int maxHeight, BufferedImage watermark) {
|
|
|
try {
|
|
|
int w = img.getImg().getWidth(null);
|
|
|
int h = img.getImg().getHeight(null);
|
|
|
if (maxWidth > 0 && maxHeight > 0) {
|
|
|
if (w > maxWidth || h > maxHeight) {
|
|
|
int outWidth = 0;
|
|
|
int outHeight = 0;
|
|
|
outHeight = maxWidth * h / w;
|
|
|
if (outHeight > maxHeight) {
|
|
|
outHeight = maxHeight;
|
|
|
outWidth = outHeight * w / h;
|
|
|
} else {
|
|
|
outWidth = maxWidth;
|
|
|
}
|
|
|
img = img.scale(outWidth, outHeight);
|
|
|
w = outWidth;
|
|
|
h = outHeight;
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (watermark != null) {
|
|
|
int ww = watermark.getWidth(null);
|
|
|
int wh = watermark.getHeight(null);
|
|
|
if (w > ww && h > wh) {
|
|
|
img = img.pressImage(watermark, 0, 0, 1f);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
ByteArrayOutputStream pout = new ByteArrayOutputStream();
|
|
|
|
|
|
img.setTargetImageType(IMAGE_WEBP).write(pout);
|
|
|
|
|
|
ByteArrayInputStream pin = new ByteArrayInputStream(pout.toByteArray());
|
|
|
pout.close();
|
|
|
pout = null;
|
|
|
return pin;
|
|
|
} catch (Exception e) {
|
|
|
throw new RuntimeException("处理图片错误", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* <pre>
|
|
|
* - 根据存放规则和文件名生成存放的URI
|
|
|
* - 支持
|
|
|
* - {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
|
|
|
* - {UUID} 32位的唯一标志
|
|
|
* - {i} 自增id
|
|
|
* - {id} 当日int类型的唯一id,防止重复建议+年月日路径
|
|
|
* - {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
|
|
|
* - {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
|
|
|
* - {filename} 文件基础名称
|
|
|
* - {ext} 扩展名
|
|
|
* </pre>
|
|
|
*
|
|
|
* @param rule
|
|
|
* @param filename
|
|
|
* @return
|
|
|
*/
|
|
|
default String generateURI(String rule, String filename) {
|
|
|
Calendar c = Calendar.getInstance();
|
|
|
if (rule.contains("{yyyy}")) {
|
|
|
rule = rule.replace("{yyyy}", "" + c.get(Calendar.YEAR));
|
|
|
}
|
|
|
if (rule.contains("{MM}")) {
|
|
|
rule = rule.replace("{MM}", String.format("%02d", c.get(Calendar.MONTH) + 1));
|
|
|
}
|
|
|
if (rule.contains("{dd}")) {
|
|
|
rule = rule.replace("{dd}", String.format("%02d", c.get(Calendar.DATE)));
|
|
|
}
|
|
|
if (rule.contains("{HH}")) {
|
|
|
rule = rule.replace("{HH}", String.format("%02d", c.get(Calendar.HOUR_OF_DAY)));
|
|
|
}
|
|
|
if (rule.contains("{mm}")) {
|
|
|
rule = rule.replace("{mm}", String.format("%02d", c.get(Calendar.MINUTE)));
|
|
|
}
|
|
|
if (rule.contains("{ss}")) {
|
|
|
rule = rule.replace("{ss}", String.format("%02d", c.get(Calendar.SECOND)));
|
|
|
}
|
|
|
if (rule.contains("{UUID}")) {
|
|
|
rule = rule.replace("{UUID}", UUID.fastUUID().toString(true));
|
|
|
}
|
|
|
if (rule.contains("{i}")) {
|
|
|
rule = rule.replace("{i}", IdUtils.nextId(Id.groupName).toString());
|
|
|
}
|
|
|
if (rule.contains("{id}")) {
|
|
|
rule = rule.replace("{id}", Long.toString(id.nextId()));
|
|
|
}
|
|
|
if (rule.contains("{id16}")) {
|
|
|
rule = rule.replace("{id16}", Long.toString(id.nextId(), 16));
|
|
|
}
|
|
|
if (rule.contains("{id36}")) {
|
|
|
rule = rule.replace("{id36}", Long.toString(id.nextId(), 36));
|
|
|
}
|
|
|
if (rule.contains("{filename}")) {
|
|
|
String temp = null;
|
|
|
if (filename.contains(".")) {
|
|
|
temp = filename.substring(0, filename.lastIndexOf("."));
|
|
|
} else {
|
|
|
temp = filename;
|
|
|
}
|
|
|
rule = rule.replace("{filename}", temp);
|
|
|
}
|
|
|
|
|
|
if (rule.contains("{ext}")) {
|
|
|
String temp = null;
|
|
|
if (filename.contains(".")) {
|
|
|
temp = filename.substring(filename.lastIndexOf(".") + 1);
|
|
|
} else {
|
|
|
temp = "";
|
|
|
}
|
|
|
rule = rule.replace("{ext}", temp.toLowerCase());
|
|
|
}
|
|
|
return rule;
|
|
|
}
|
|
|
|
|
|
Id id = new Id();
|
|
|
|
|
|
static class Id {
|
|
|
|
|
|
private static final String groupName = "file:id";
|
|
|
|
|
|
private SymmetricCrypto crypto;
|
|
|
|
|
|
private String today = DateUtil.today();
|
|
|
|
|
|
public synchronized Long nextId() {
|
|
|
if (crypto == null || !today.equals(DateUtil.today())) {
|
|
|
today = DateUtil.today();
|
|
|
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), today.getBytes()).getEncoded();
|
|
|
crypto = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
|
|
|
}
|
|
|
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
|
|
|
buffer.putInt(IdUtils.nextDayId(groupName).intValue());
|
|
|
return Math.abs(ByteBuffer.wrap(crypto.encrypt(buffer.array())).getLong());
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// public static void main(String[] args) {
|
|
|
// byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(),DateUtil.today().getBytes()).getEncoded();
|
|
|
// System.out.println(HexUtil.encodeHexStr(key));
|
|
|
//// byte[] key = HexUtil.decodeHex("a359f3fe88445c192f20d573c80af163");
|
|
|
// SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
|
|
|
//
|
|
|
//
|
|
|
// ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
|
|
|
//
|
|
|
// System.out.println(buffer.array().length);
|
|
|
// System.out.println(aes.encrypt(buffer.array()).length);
|
|
|
// buffer.clear();
|
|
|
// buffer.putInt(1);
|
|
|
// System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong());
|
|
|
// buffer.clear();
|
|
|
// buffer.putInt(2);
|
|
|
// System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong());
|
|
|
// buffer.clear();
|
|
|
// buffer.putInt(3);
|
|
|
// System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong());
|
|
|
// }
|
|
|
|
|
|
SysOssVo url(SysOssVo oss, int second);
|
|
|
|
|
|
default SysOssVo url(SysOssVo oss) {
|
|
|
return url(oss, 120);
|
|
|
}
|
|
|
|
|
|
String url(Service service,String url,int second);
|
|
|
|
|
|
default String url(Service service,String url) {
|
|
|
return url(service,url, 120);
|
|
|
}
|
|
|
}
|