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 setService(Service service, Supplier handle); /** * 只保存,不存放记录 * * @param handle 处理执行方法 */ void ignore(Runnable handle); /** * 只保存,不存放记录 * * @param handle 处理执行方法 */ public T ignore(Supplier handle); TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); List listByIds(Collection 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: *
   * - 根据存放规则和文件名生成存放的URI
   * - 支持
   * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
   * 	- {UUID} 32位的唯一标志
   * 	- {i} 自增id
   * 	- {id} 当日int类型的唯一id,防止重复建议+年月日路径
   * 	- {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
   * 	- {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
   * 	- {filename} 文件基础名称
   * 	- {ext} 扩展名
   * 
* * @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 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); } } /** *
   * - 根据存放规则和文件名生成存放的URI
   * - 支持
   * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
   * 	- {UUID} 32位的唯一标志
   *  - {i} 自增id
   * 	- {id} 当日int类型的唯一id,防止重复建议+年月日路径
   * 	- {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
   * 	- {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
   * 	- {filename} 文件基础名称
   * 	- {ext} 扩展名
   * 
* * @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); } }