فهرست منبع

新增文件上传/下载(本地)

tsurumure 5 ماه پیش
والد
کامیت
511ac2b369

+ 1 - 1
db/sys_common.sql

@@ -21,7 +21,7 @@ CREATE TABLE `sys_common` (
 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';
 
 INSERT INTO sys_common(name, description, tag, value, value_type, value_component, value_option, category, sort) VALUES
-    ('上传存储介质', '使用本地或云存储桶来存放文件', 'UPLOAD_TARGET',  3, 'Integer', 'Radio', '[{\"label\":\"本地\",\"value\":-1,\"disabled\":true},{\"label\":\"腾讯云\",\"value\":1,\"disabled\":false},{\"label\":\"阿里云\",\"value\":2,\"disabled\":true},{\"label\":\"抖音云\",\"value\":3,\"disabled\":false}]', 'UPLOAD', 3),
+    ('上传存储介质', '使用本地或云存储桶来存放文件', 'UPLOAD_TARGET',  3, 'Integer', 'Radio', '[{\"label\":\"本地\",\"value\":-1,\"disabled\":false},{\"label\":\"腾讯云\",\"value\":1,\"disabled\":false},{\"label\":\"阿里云\",\"value\":2,\"disabled\":true},{\"label\":\"抖音云\",\"value\":3,\"disabled\":false}]', 'UPLOAD', 3),
     ('单文件上传大小限制(MB)', null, 'UPLOAD_MAX_SIZE_MB', 100, 'Integer', 'Number', null, 'UPLOAD', 2),
     ('是否启用文件MD5查重', '已存在的文件不再重复上传,仅返回链接', 'UPLOAD_MD5_DUPLICATE', 1, 'Integer', 'Switch', null, 'UPLOAD', 1),
 

+ 139 - 139
src/main/java/com/backendsys/controller/Upload/UploadAliOSSController.java

@@ -1,139 +1,139 @@
-package com.backendsys.controller.Upload;
-
-import com.aliyun.oss.OSS;
-import com.aliyun.oss.OSSClientBuilder;
-import com.aliyun.oss.OSSException;
-import com.aliyun.oss.model.PutObjectRequest;
-import com.aliyun.oss.model.PutObjectResult;
-import com.aliyuncs.exceptions.ClientException;
-import com.backendsys.utils.CommonUtil;
-import com.backendsys.utils.response.Result;
-import com.backendsys.utils.response.ResultEnum;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-@RestController
-public class UploadAliOSSController {
-
-    /**
-     * 文档:图片处理(水印)(大小)
-     * https://help.aliyun.com/zh/oss/developer-reference/img-2?spm=a2c4g.11186623.0.0.28dc3af2212Yhe
-     *
-     * 注意:需要绑定自定义域名,才能获得图片浏览器访问的效果
-     * https://help.aliyun.com/zh/oss/user-guide/how-to-ensure-an-object-is-previewed-when-you-access-the-object?spm=a2c4g.11186623.0.0.60c2431eiMgwZB
-     * 图片地址示例:
-     * https://backendsys.oss-cn-shenzhen.aliyuncs.com/20230812/20230812132756.png
-     */
-
-    @Value("${aliyun.oss.max-size}")
-    private long maxSize;
-    @Value("${aliyun.oss.access-key-id}")
-    private String accessKeyId;
-    @Value("${aliyun.oss.access-key-secret}")
-    private String accessKeySecret;
-    @Value("${aliyun.oss.endpoint}")
-    private String endpoint;
-    @Value("${aliyun.oss.bucket-name}")
-    private String bucketName;
-    @Value("${aliyun.oss.accessible-domain}")
-    private String accessibleDomain;
-
-    /**
-     * 阿里云 OSS
-     */
-    @PreAuthorize("@sr.hasPermission(1.1)")
-    @PostMapping("/api/ali/ossUpload")
-    public Result upload(@RequestParam("file") MultipartFile file) throws ClientException {
-
-        // 检查上传的文件是否为空
-        if (file.isEmpty()) {
-            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能为空");
-        }
-        // 判断文件大小是否超过
-        if (file.getSize() > maxSize) {
-            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能大于 " + maxSize/1024/1024 + " MB");
-        }
-
-        //System.out.println("accessKeyId = " + accessKeyId);
-        //System.out.println("accessKeySecret = " + accessKeySecret);
-        //System.out.println("endpoint = " + endpoint);
-        //System.out.println("bucketName = " + bucketName);
-
-        // 填写Object完整路径,例如exampledir/exampleobject.txt。
-        // 按日期作为文件目录
-        String dateDir = new SimpleDateFormat("yyyyMMdd").format(new Date()) + File.separator;
-
-        // 获得后缀名
-        String originalFileName = file.getOriginalFilename();
-        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
-
-        // 生成新的文件名
-        String newFileName = CommonUtil.generateFilename(suffix);
-
-        //
-        String objectName = dateDir + newFileName;
-
-        // 创建OSSClient实例
-        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
-
-        // 简单上传文件
-        try {
-            // 获取文件内容
-            InputStream inputStream = file.getInputStream();
-            // 创建PutObjectRequest对象。
-            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);
-            // 创建PutObject请求。
-            PutObjectResult result = ossClient.putObject(putObjectRequest);
-
-        } catch (OSSException oe) {
-            System.out.println("Caught an OSSException, which means your request made it to OSS, "
-                    + "but was rejected with an error response for some reason.");
-            System.out.println("Error Message:" + oe.getErrorMessage());
-            System.out.println("Error Code:" + oe.getErrorCode());
-            System.out.println("Request ID:" + oe.getRequestId());
-            System.out.println("Host ID:" + oe.getHostId());
-        } catch (IOException ce) {
-            System.out.println(ce);
-        } finally {
-            if (ossClient != null) {
-                ossClient.shutdown();
-            }
-        }
-
-        // 获取临时访问权限
-        //GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
-        //Date expiration = new Date(new Date().getTime() + 3600 * 1000);
-        //request.setExpiration(expiration);
-        //////方法一: 直接覆盖请求头
-        ////ResponseHeaderOverrides Headers=new ResponseHeaderOverrides();
-        ////Headers.setContentDisposition(String.format("attachment;filename=%s", fileName));
-        ////request.setResponseHeaders(Headers);
-        ////方法二: 设置setQueryParameter();其实方法一源码也是这样设置的
-        // Map<String, String> queryParams = new LinkedHashMap<>(8);
-        // queryParams.put("response-content-disposition", String.format("attachment;filename=%s", newFileName));
-        // request.setQueryParameter(queryParams);
-        // URL url = ossClient.generatePresignedUrl(request);
-
-        Map<String, Object> map = new LinkedHashMap<>();
-        map.put("filename", newFileName);
-        map.put("url", accessibleDomain + File.separator + objectName);
-        map.put("type", suffix.replace(".", ""));
-        map.put("size", file.getSize());
-
-        return Result.success(map);
-    }
-
-
-}
+//package com.backendsys.controller.Upload;
+//
+//import com.aliyun.oss.OSS;
+//import com.aliyun.oss.OSSClientBuilder;
+//import com.aliyun.oss.OSSException;
+//import com.aliyun.oss.model.PutObjectRequest;
+//import com.aliyun.oss.model.PutObjectResult;
+//import com.aliyuncs.exceptions.ClientException;
+//import com.backendsys.utils.CommonUtil;
+//import com.backendsys.utils.response.Result;
+//import com.backendsys.utils.response.ResultEnum;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.security.access.prepost.PreAuthorize;
+//import org.springframework.web.bind.annotation.PostMapping;
+//import org.springframework.web.bind.annotation.RequestParam;
+//import org.springframework.web.bind.annotation.RestController;
+//import org.springframework.web.multipart.MultipartFile;
+//
+//import java.io.File;
+//import java.io.IOException;
+//import java.io.InputStream;
+//import java.text.SimpleDateFormat;
+//import java.util.Date;
+//import java.util.LinkedHashMap;
+//import java.util.Map;
+//
+//@RestController
+//public class UploadAliOSSController {
+//
+//    /**
+//     * 文档:图片处理(水印)(大小)
+//     * https://help.aliyun.com/zh/oss/developer-reference/img-2?spm=a2c4g.11186623.0.0.28dc3af2212Yhe
+//     *
+//     * 注意:需要绑定自定义域名,才能获得图片浏览器访问的效果
+//     * https://help.aliyun.com/zh/oss/user-guide/how-to-ensure-an-object-is-previewed-when-you-access-the-object?spm=a2c4g.11186623.0.0.60c2431eiMgwZB
+//     * 图片地址示例:
+//     * https://backendsys.oss-cn-shenzhen.aliyuncs.com/20230812/20230812132756.png
+//     */
+//
+//    @Value("${aliyun.oss.max-size}")
+//    private long maxSize;
+//    @Value("${aliyun.oss.access-key-id}")
+//    private String accessKeyId;
+//    @Value("${aliyun.oss.access-key-secret}")
+//    private String accessKeySecret;
+//    @Value("${aliyun.oss.endpoint}")
+//    private String endpoint;
+//    @Value("${aliyun.oss.bucket-name}")
+//    private String bucketName;
+//    @Value("${aliyun.oss.accessible-domain}")
+//    private String accessibleDomain;
+//
+//    /**
+//     * 阿里云 OSS
+//     */
+//    @PreAuthorize("@sr.hasPermission(1.1)")
+//    @PostMapping("/api/ali/ossUpload")
+//    public Result upload(@RequestParam("file") MultipartFile file) throws ClientException {
+//
+//        // 检查上传的文件是否为空
+//        if (file.isEmpty()) {
+//            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能为空");
+//        }
+//        // 判断文件大小是否超过
+//        if (file.getSize() > maxSize) {
+//            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能大于 " + maxSize/1024/1024 + " MB");
+//        }
+//
+//        //System.out.println("accessKeyId = " + accessKeyId);
+//        //System.out.println("accessKeySecret = " + accessKeySecret);
+//        //System.out.println("endpoint = " + endpoint);
+//        //System.out.println("bucketName = " + bucketName);
+//
+//        // 填写Object完整路径,例如exampledir/exampleobject.txt。
+//        // 按日期作为文件目录
+//        String dateDir = new SimpleDateFormat("yyyyMMdd").format(new Date()) + File.separator;
+//
+//        // 获得后缀名
+//        String originalFileName = file.getOriginalFilename();
+//        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
+//
+//        // 生成新的文件名
+//        String newFileName = CommonUtil.generateFilename(suffix);
+//
+//        //
+//        String objectName = dateDir + newFileName;
+//
+//        // 创建OSSClient实例
+//        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+//
+//        // 简单上传文件
+//        try {
+//            // 获取文件内容
+//            InputStream inputStream = file.getInputStream();
+//            // 创建PutObjectRequest对象。
+//            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);
+//            // 创建PutObject请求。
+//            PutObjectResult result = ossClient.putObject(putObjectRequest);
+//
+//        } catch (OSSException oe) {
+//            System.out.println("Caught an OSSException, which means your request made it to OSS, "
+//                    + "but was rejected with an error response for some reason.");
+//            System.out.println("Error Message:" + oe.getErrorMessage());
+//            System.out.println("Error Code:" + oe.getErrorCode());
+//            System.out.println("Request ID:" + oe.getRequestId());
+//            System.out.println("Host ID:" + oe.getHostId());
+//        } catch (IOException ce) {
+//            System.out.println(ce);
+//        } finally {
+//            if (ossClient != null) {
+//                ossClient.shutdown();
+//            }
+//        }
+//
+//        // 获取临时访问权限
+//        //GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
+//        //Date expiration = new Date(new Date().getTime() + 3600 * 1000);
+//        //request.setExpiration(expiration);
+//        //////方法一: 直接覆盖请求头
+//        ////ResponseHeaderOverrides Headers=new ResponseHeaderOverrides();
+//        ////Headers.setContentDisposition(String.format("attachment;filename=%s", fileName));
+//        ////request.setResponseHeaders(Headers);
+//        ////方法二: 设置setQueryParameter();其实方法一源码也是这样设置的
+//        // Map<String, String> queryParams = new LinkedHashMap<>(8);
+//        // queryParams.put("response-content-disposition", String.format("attachment;filename=%s", newFileName));
+//        // request.setQueryParameter(queryParams);
+//        // URL url = ossClient.generatePresignedUrl(request);
+//
+//        Map<String, Object> map = new LinkedHashMap<>();
+//        map.put("filename", newFileName);
+//        map.put("url", accessibleDomain + File.separator + objectName);
+//        map.put("type", suffix.replace(".", ""));
+//        map.put("size", file.getSize());
+//
+//        return Result.success(map);
+//    }
+//
+//
+//}

+ 119 - 119
src/main/java/com/backendsys/controller/Upload/UploadLocalController.java

@@ -1,119 +1,119 @@
-package com.backendsys.controller.Upload;
-
-import com.backendsys.utils.CommonUtil;
-import com.backendsys.utils.response.Result;
-import com.backendsys.utils.response.ResultEnum;
-import net.coobird.thumbnailator.Thumbnails;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.*;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * 上传 (本地)
- */
-@RestController
-public class UploadLocalController {
-
-    @Value("${file.upload.directory}") // 配置保存文件的目录
-    private String uploadDirectory;
-
-    @Value("${file.upload.url-prefix}") // 指定文件访问的 URL前缀
-    private String uploadUrlPrefix;
-
-    @Value("${file.upload.max-size}")
-    private long maxSize;
-
-    @PreAuthorize("@sr.hasPermission(1)")
-    @PostMapping("/api/upload")
-    public Result uploadLocal(@RequestParam("file") MultipartFile file) {
-
-        // 检查上传的文件是否为空
-        if (file.isEmpty()) {
-            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能为空");
-        }
-        // 判断文件大小是否超过
-        if (file.getSize() > maxSize) {
-            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能超过 " + maxSize/1024/1024 + " MB,请使用大文件上传功能");
-        }
-
-        try {
-            // 创建保存文件的目录,如果不存在
-            File directory = new File(uploadDirectory);
-            if (!directory.exists()) { directory.mkdirs(); }
-
-            // 获取文件原始名
-            String originalFileName = file.getOriginalFilename();
-
-            // 生成后缀名
-            String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
-
-            // 生成新的文件名
-            String newFileName = CommonUtil.generateFilename(suffix);
-            String newFileNameThumb = newFileName.replaceAll(suffix, "-thumb" + suffix);
-
-            // 按日期作为文件目录
-            String pathDir = new SimpleDateFormat("yyyyMMdd").format(new Date());
-            String dateDir = pathDir + File.separator;
-
-            File dateDirectory = new File(uploadDirectory + dateDir);
-            if (!dateDirectory.exists()) { dateDirectory.mkdirs(); }
-
-//            System.out.println("uploadDirectory = " + uploadDirectory);
-//            System.out.println("dateDir = " + dateDir);
-//            System.out.println("newFileName = " + newFileName);
-
-            // 确定文件保存的路径
-            String sourceFilePath = uploadDirectory + dateDir + newFileName;
-            String sourceFilePathThumb = uploadDirectory + dateDir + newFileNameThumb;
-
-            System.out.println(sourceFilePath);
-            System.out.println(sourceFilePathThumb);
-
-            File sourceFile = new File(sourceFilePath);
-//            File sourceFileThumb = new File(sourceFilePathThumb);
-
-            // 保存文件到本地
-            file.transferTo(sourceFile);
-
-
-            // 创建缩略图
-            // .scale(0.5)  // 缩放
-            // .outputFormat("png") // 类型
-            // .sourceRegion(Positions.TOP_RIGHT, 1800, 1800) // 裁剪
-            // .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("watermark.png")), 0.5f)  // 添加水印
-            // .outputQuality(0.2) // 质量
-            // 文件批量操作
-            // Thumbnails.of(new File("/images/202401/").listFiles()).size(400, 400).toFiles(Rename.PREFIX_DOT_THUMBNAIL);
-            Thumbnails.of(sourceFilePath).size(200, 200).toFile(sourceFilePathThumb);
-
-
-            // 返回文件的路径 (绝对路径)
-            String fileUrl = uploadUrlPrefix + pathDir + "/" + newFileName;
-            String fileThumbUrl = uploadUrlPrefix + pathDir + "/" + newFileNameThumb;
-
-            Map<String, Object> map = new LinkedHashMap<>();
-            map.put("filename", newFileName);
-            map.put("url", fileUrl);
-            map.put("url_thumb", fileThumbUrl);
-            map.put("type", suffix.replace(".", ""));
-            map.put("size", sourceFile.length());
-
-            // 返回响应
-            return Result.success(map);
-
-        } catch (IOException e) {
-            e.printStackTrace();
-            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "上传失败");
-        }
-    }
-
-}
+//package com.backendsys.controller.Upload;
+//
+//import com.backendsys.utils.CommonUtil;
+//import com.backendsys.utils.response.Result;
+//import com.backendsys.utils.response.ResultEnum;
+//import net.coobird.thumbnailator.Thumbnails;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.security.access.prepost.PreAuthorize;
+//import org.springframework.web.bind.annotation.PostMapping;
+//import org.springframework.web.bind.annotation.RequestParam;
+//import org.springframework.web.bind.annotation.RestController;
+//import org.springframework.web.multipart.MultipartFile;
+//
+//import java.io.*;
+//import java.text.SimpleDateFormat;
+//import java.util.Date;
+//import java.util.LinkedHashMap;
+//import java.util.Map;
+//
+///**
+// * 上传 (本地)
+// */
+//@RestController
+//public class UploadLocalController {
+//
+//    @Value("${file.upload.directory}") // 配置保存文件的目录
+//    private String uploadDirectory;
+//
+//    @Value("${file.upload.url-prefix}") // 指定文件访问的 URL前缀
+//    private String uploadUrlPrefix;
+//
+//    @Value("${file.upload.max-size}")
+//    private long maxSize;
+//
+//    @PreAuthorize("@sr.hasPermission(1)")
+//    @PostMapping("/api/upload")
+//    public Result uploadLocal(@RequestParam("file") MultipartFile file) {
+//
+//        // 检查上传的文件是否为空
+//        if (file.isEmpty()) {
+//            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能为空");
+//        }
+//        // 判断文件大小是否超过
+//        if (file.getSize() > maxSize) {
+//            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "文件不能超过 " + maxSize/1024/1024 + " MB,请使用大文件上传功能");
+//        }
+//
+//        try {
+//            // 创建保存文件的目录,如果不存在
+//            File directory = new File(uploadDirectory);
+//            if (!directory.exists()) { directory.mkdirs(); }
+//
+//            // 获取文件原始名
+//            String originalFileName = file.getOriginalFilename();
+//
+//            // 生成后缀名
+//            String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
+//
+//            // 生成新的文件名
+//            String newFileName = CommonUtil.generateFilename(suffix);
+//            String newFileNameThumb = newFileName.replaceAll(suffix, "-thumb" + suffix);
+//
+//            // 按日期作为文件目录
+//            String pathDir = new SimpleDateFormat("yyyyMMdd").format(new Date());
+//            String dateDir = pathDir + File.separator;
+//
+//            File dateDirectory = new File(uploadDirectory + dateDir);
+//            if (!dateDirectory.exists()) { dateDirectory.mkdirs(); }
+//
+////            System.out.println("uploadDirectory = " + uploadDirectory);
+////            System.out.println("dateDir = " + dateDir);
+////            System.out.println("newFileName = " + newFileName);
+//
+//            // 确定文件保存的路径
+//            String sourceFilePath = uploadDirectory + dateDir + newFileName;
+//            String sourceFilePathThumb = uploadDirectory + dateDir + newFileNameThumb;
+//
+//            System.out.println(sourceFilePath);
+//            System.out.println(sourceFilePathThumb);
+//
+//            File sourceFile = new File(sourceFilePath);
+////            File sourceFileThumb = new File(sourceFilePathThumb);
+//
+//            // 保存文件到本地
+//            file.transferTo(sourceFile);
+//
+//
+//            // 创建缩略图
+//            // .scale(0.5)  // 缩放
+//            // .outputFormat("png") // 类型
+//            // .sourceRegion(Positions.TOP_RIGHT, 1800, 1800) // 裁剪
+//            // .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("watermark.png")), 0.5f)  // 添加水印
+//            // .outputQuality(0.2) // 质量
+//            // 文件批量操作
+//            // Thumbnails.of(new File("/images/202401/").listFiles()).size(400, 400).toFiles(Rename.PREFIX_DOT_THUMBNAIL);
+//            Thumbnails.of(sourceFilePath).size(200, 200).toFile(sourceFilePathThumb);
+//
+//
+//            // 返回文件的路径 (绝对路径)
+//            String fileUrl = uploadUrlPrefix + pathDir + "/" + newFileName;
+//            String fileThumbUrl = uploadUrlPrefix + pathDir + "/" + newFileNameThumb;
+//
+//            Map<String, Object> map = new LinkedHashMap<>();
+//            map.put("filename", newFileName);
+//            map.put("url", fileUrl);
+//            map.put("url_thumb", fileThumbUrl);
+//            map.put("type", suffix.replace(".", ""));
+//            map.put("size", sourceFile.length());
+//
+//            // 返回响应
+//            return Result.success(map);
+//
+//        } catch (IOException e) {
+//            e.printStackTrace();
+//            return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), "上传失败");
+//        }
+//    }
+//
+//}

+ 13 - 13
src/main/java/com/backendsys/mapper/System/SysMobileSMSHistoryMapper.java

@@ -1,13 +1,13 @@
-package com.backendsys.mapper.System;
-
-import com.backendsys.entity.System.SysSMSDTO;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-import java.util.Map;
-
-@Mapper
-public interface SysMobileSMSHistoryMapper {
-    List<Map<String, Object>> queryMobileSMSHistoryList(SysSMSDTO sysSMSDTO);
-    long insertMobileSMSHistory(SysSMSDTO sysSMSDTO);
-}
+//package com.backendsys.mapper.System;
+//
+//import com.backendsys.entity.System.SysSMSDTO;
+//import org.apache.ibatis.annotations.Mapper;
+//
+//import java.util.List;
+//import java.util.Map;
+//
+//@Mapper
+//public interface SysMobileSMSHistoryMapper {
+//    List<Map<String, Object>> queryMobileSMSHistoryList(SysSMSDTO sysSMSDTO);
+//    long insertMobileSMSHistory(SysSMSDTO sysSMSDTO);
+//}

+ 59 - 59
src/main/java/com/backendsys/mapper/System/SysMobileSMSHistoryMapper.xml

@@ -1,64 +1,64 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
-<mapper namespace="com.backendsys.mapper.System.SysMobileSMSHistoryMapper">
+<!--<?xml version="1.0" encoding="utf-8" ?>-->
+<!--<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >-->
+<!--<mapper namespace="com.backendsys.mapper.System.SysMobileSMSHistoryMapper">-->
 
-    <sql id="includeMobileSMSHistory">
-        id,
-        COALESCE(origin, '') origin,
-        phone,
-        COALESCE(sms_code, '') sms_code,
-        COALESCE(ip, '') ip,
-        COALESCE(ua, '') ua,
-        create_time
-    </sql>
+<!--    <sql id="includeMobileSMSHistory">-->
+<!--        id,-->
+<!--        COALESCE(origin, '') origin,-->
+<!--        phone,-->
+<!--        COALESCE(sms_code, '') sms_code,-->
+<!--        COALESCE(ip, '') ip,-->
+<!--        COALESCE(ua, '') ua,-->
+<!--        create_time-->
+<!--    </sql>-->
 
-    <resultMap id="resultMapMobileSMSHistoryList" type="java.util.LinkedHashMap">
-        <id property="id" column="id" jdbcType="BIGINT" />
-        <result property="origin" column="origin" />
-        <result property="phone" column="phone" />
-        <result property="sms_code" column="sms_code" />
-        <result property="ip" column="ip" />
-        <result property="ua" column="ua" />
-        <result property="create_time" column="create_time" />
-    </resultMap>
+<!--    <resultMap id="resultMapMobileSMSHistoryList" type="java.util.LinkedHashMap">-->
+<!--        <id property="id" column="id" jdbcType="BIGINT" />-->
+<!--        <result property="origin" column="origin" />-->
+<!--        <result property="phone" column="phone" />-->
+<!--        <result property="sms_code" column="sms_code" />-->
+<!--        <result property="ip" column="ip" />-->
+<!--        <result property="ua" column="ua" />-->
+<!--        <result property="create_time" column="create_time" />-->
+<!--    </resultMap>-->
 
-    <!-- 查询 列表 -->
-    <select id="queryMobileSMSHistoryList" resultMap="resultMapMobileSMSHistoryList">
-        SELECT <include refid="includeMobileSMSHistory" />
-        FROM sys_mobile_sms_history
-        <where>
-            <if test="origin != null and origin != ''">
-                AND origin = #{origin}
-            </if>
-            <if test="phone != null and phone != ''">
-                AND phone = #{phone}
-            </if>
-            <if test="ip != null and ip != ''">
-                AND ip = #{ip}
-            </if>
-            <if test="create_time_begin != null and create_time_begin != '' and create_time_end != null and create_time_end != ''">
-                AND create_time BETWEEN #{create_time_begin} AND #{create_time_end}
-            </if>
-        </where>
-        ORDER BY create_time DESC
-    </select>
+<!--    &lt;!&ndash; 查询 列表 &ndash;&gt;-->
+<!--    <select id="queryMobileSMSHistoryList" resultMap="resultMapMobileSMSHistoryList">-->
+<!--        SELECT <include refid="includeMobileSMSHistory" />-->
+<!--        FROM sys_mobile_sms_history-->
+<!--        <where>-->
+<!--            <if test="origin != null and origin != ''">-->
+<!--                AND origin = #{origin}-->
+<!--            </if>-->
+<!--            <if test="phone != null and phone != ''">-->
+<!--                AND phone = #{phone}-->
+<!--            </if>-->
+<!--            <if test="ip != null and ip != ''">-->
+<!--                AND ip = #{ip}-->
+<!--            </if>-->
+<!--            <if test="create_time_begin != null and create_time_begin != '' and create_time_end != null and create_time_end != ''">-->
+<!--                AND create_time BETWEEN #{create_time_begin} AND #{create_time_end}-->
+<!--            </if>-->
+<!--        </where>-->
+<!--        ORDER BY create_time DESC-->
+<!--    </select>-->
 
-    <!-- 创建 -->
-    <insert id="insertMobileSMSHistory" parameterType="com.backendsys.entity.System.SysSMSDTO"
-        useGeneratedKeys="true" keyProperty="id">
-        INSERT INTO sys_mobile_sms_history (
-            phone
-            <if test="origin != null and origin != ''">, origin</if>
-            <if test="sms_code != null and sms_code != ''">, sms_code</if>
-            <if test="ip != null and ip != ''">, ip</if>
-            <if test="ua != null and ua != ''">, ua</if>
-        ) VALUES (
-            #{phone}
-            <if test="origin != null and origin != ''">, #{origin}</if>
-            <if test="sms_code != null and sms_code != ''">, #{sms_code}</if>
-            <if test="ip != null and ip != ''">, #{ip}</if>
-            <if test="ua != null and ua != ''">, #{ua}</if>
-        )
-    </insert>
+<!--    &lt;!&ndash; 创建 &ndash;&gt;-->
+<!--    <insert id="insertMobileSMSHistory" parameterType="com.backendsys.entity.System.SysSMSDTO"-->
+<!--        useGeneratedKeys="true" keyProperty="id">-->
+<!--        INSERT INTO sys_mobile_sms_history (-->
+<!--            phone-->
+<!--            <if test="origin != null and origin != ''">, origin</if>-->
+<!--            <if test="sms_code != null and sms_code != ''">, sms_code</if>-->
+<!--            <if test="ip != null and ip != ''">, ip</if>-->
+<!--            <if test="ua != null and ua != ''">, ua</if>-->
+<!--        ) VALUES (-->
+<!--            #{phone}-->
+<!--            <if test="origin != null and origin != ''">, #{origin}</if>-->
+<!--            <if test="sms_code != null and sms_code != ''">, #{sms_code}</if>-->
+<!--            <if test="ip != null and ip != ''">, #{ip}</if>-->
+<!--            <if test="ua != null and ua != ''">, #{ua}</if>-->
+<!--        )-->
+<!--    </insert>-->
 
-</mapper>
+<!--</mapper>-->

+ 86 - 23
src/main/java/com/backendsys/modules/download/controller/DownloadLocalController.java

@@ -1,15 +1,21 @@
 package com.backendsys.modules.download.controller;
 
+import cn.hutool.core.io.FileTypeUtil;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.InputStreamResource;
 import org.springframework.core.io.Resource;
 import org.springframework.http.*;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
 import java.io.*;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.util.Arrays;
 
 /**
@@ -21,11 +27,11 @@ public class DownloadLocalController {
     @Value("${file.upload.directory}") // 配置保存文件的目录
     private String uploadDirectory;
 
-    // 判断文件是否为图片文件
-    private boolean isImageFile(File file) {
-        String extension = getExtension(file.getName());
-        return Arrays.asList("jpg", "jpeg", "png", "gif").contains(extension.toLowerCase());
-    }
+//    // 判断文件是否为图片文件
+//    private boolean isImageFile(File file) {
+//        String extension = getExtension(file.getName());
+//        return Arrays.asList("jpg", "jpeg", "png", "gif").contains(extension.toLowerCase());
+//    }
 
     // 获取图片文件对应的MediaType
     private MediaType getImageMediaType(File file) {
@@ -48,35 +54,92 @@ public class DownloadLocalController {
         return "";
     }
 
+    private BufferedImage resizeImage(File file, Integer width, Integer height) throws IOException {
+        BufferedImage originalImage = ImageIO.read(file);
+        int newWidth = width != null ? width : originalImage.getWidth();
+        int newHeight = height != null ? height : originalImage.getHeight();
+        BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g = resizedImage.createGraphics();
+        g.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
+        g.dispose();
+        return resizedImage;
+    }
+
+    private InputStream imageToInputStream(BufferedImage image, String format) throws IOException {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        ImageIO.write(image, format, os);
+        return new ByteArrayInputStream(os.toByteArray());
+    }
+
+    /**
+     * 静态文件访问地址
+     * @param folder
+     * @param filename
+     * @param width     缩略图宽度 不大于 600
+     * @param height    缩略图高度 不大于 600
+     */
     @GetMapping("/uploads/{folder}/{filename:.+}")
-    public ResponseEntity<Resource> serveFile(@PathVariable String folder, @PathVariable String filename) throws IOException {
+    public ResponseEntity<Resource> serveFile(
+        @PathVariable String folder,
+        @PathVariable String filename,
+        @RequestParam(value = "w", required = false) Integer width,
+        @RequestParam(value = "h", required = false) Integer height
+    ) throws IOException {
+
+        Integer MAX_WIDTH = 600;
+        Integer MAX_HEIGHT = 600;
 
         File file = new File(uploadDirectory + folder + "/" + filename);
-        // 如果是图片,则直接显示;如果是文件,则是下载
+
+        if (!file.exists()) {
+            String message = "图片不存在";
+            InputStream inputStream = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8));
+            InputStreamResource resource = new InputStreamResource(inputStream);
+            return ResponseEntity.status(HttpStatus.NOT_FOUND)
+                .contentType(MediaType.valueOf("text/plain; charset=UTF-8"))
+                .body(resource);
+        }
+
         HttpHeaders headers = new HttpHeaders();
-        if (isImageFile(file)) {
+
+        // 判断文件类型
+        // - 如果是图片,则直接显示;
+        // - 如果是文件,则是下载
+        String mimeType = Files.probeContentType(file.toPath());
+        boolean isImage = mimeType != null && mimeType.startsWith("image/");
+
+        System.out.println("isImage = " + isImage);
+        if (isImage) {
+
             // 如果是图片文件,设置Content-Type为对应的图片MIME类型
             headers.setContentType(getImageMediaType(file));
+
+            // 如果请求了图片压缩
+            if (width != null || height != null) {
+                // 如果入参宽/高超出限制,则仍是返回文件下载
+                if ((width != null && width > MAX_WIDTH) || (height != null && height > MAX_HEIGHT)) {
+                    // 如果不是图片文件,设置 Content-Disposition 以附件形式下载
+                    headers.setContentDisposition(ContentDisposition.attachment().filename(file.getName()).build());
+                    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+                } else {
+                    // 图片显示
+                    BufferedImage resizedImage = resizeImage(file, width, height);
+                    // 根据文件类型动态选择图片格式
+                    String imageFormat = mimeType.split("/")[1]; // 提取图片格式,如 "png"、"jpeg" 等
+                    return ResponseEntity.ok().headers(headers)
+                        .contentLength(resizedImage.getWidth() * resizedImage.getHeight() * 3) // 假设是RGB
+                        .body(new InputStreamResource(imageToInputStream(resizedImage, imageFormat)));
+                }
+            }
+
         } else {
-            // 如果不是图片文件,设置Content-Disposition以附件形式下载
+            // 如果不是图片文件,设置 Content-Disposition 以附件形式下载
             headers.setContentDisposition(ContentDisposition.attachment().filename(file.getName()).build());
             headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
         }
-        if (!file.exists()) {
-            String message = "图片不存在";
-            InputStream inputStream = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8));
-            InputStreamResource resource = new InputStreamResource(inputStream);
-            return ResponseEntity.status(HttpStatus.NOT_FOUND)
-                    .contentType(MediaType.valueOf("text/plain; charset=UTF-8"))
-                    .body(resource);
-        }
+
         InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
-        return ResponseEntity.ok()
-                .headers(headers)
-                .contentLength(file.length())
-                //.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
-                //.contentType(MediaType.APPLICATION_OCTET_STREAM)
-                .body(resource);
+        return ResponseEntity.ok().headers(headers).contentLength(file.length()).body(resource);
     }
 
 }

+ 22 - 1
src/main/java/com/backendsys/modules/upload/service/impl/SysFileServiceImpl.java

@@ -16,6 +16,7 @@ import com.backendsys.modules.upload.dao.SysFileDao;
 import com.backendsys.modules.upload.entity.SysFile;
 import com.backendsys.modules.upload.entity.SysFileResult;
 import com.backendsys.modules.upload.service.SysFileService;
+import com.backendsys.modules.upload.utils.UploadUtil;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.response.PageInfoResult;
 import com.backendsys.utils.v2.PageUtils;
@@ -45,6 +46,8 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
     @Autowired
     private DouyinTosService douyinTosService;
 
+    @Autowired
+    private UploadUtil uploadUtil;
     @Autowired
     private SysFileDao sysFileDao;
     @Autowired
@@ -69,6 +72,11 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
         // - 3:抖音云: https://www.volcengine.com/docs/6349/153626
         sysFileList = sysFileList.stream().map(item -> {
 
+//            // 本地上传
+//            if (item.getTarget() == -1) {
+//                item.set
+//            }
+
             // 腾讯云 (color值通过base64加密, #f8f8f8)
             if (item.getTarget() == 1) {
                 System.out.println("base64 encode: " + Base64.encode(backgroundColor));
@@ -91,6 +99,9 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
             SysFileResult uploadResult = new SysFileResult();
 
             // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
+            if (target == -1) {
+                uploadResult = uploadUtil.put(multipartFile);
+            }
             if (target == 1) {
                 // [腾讯云-上传对象]
                 uploadResult = tencentCosService.putObject(multipartFile, null);
@@ -133,7 +144,13 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
 
 
             sysFileEntity.setContent_type(multipartFile.getContentType());
-            sysFileEntity.setUrl(uploadResult.getDomain() + "/" + uploadResult.getKey());
+            if (target != -1) {
+                // 非本地的 (Cos, Tos, ..)
+                sysFileEntity.setUrl(uploadResult.getDomain() + "/" + uploadResult.getKey());
+            } else {
+                // 本地的
+                sysFileEntity.setUrl(uploadResult.getDomain());
+            }
             sysFileEntity.setSize(multipartFile.getSize());
             if (StrUtil.isNotEmpty(uploadResult.getE_tag())) {
                 sysFileEntity.setMd5(uploadResult.getE_tag().replace("\"", ""));
@@ -221,6 +238,10 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
 
     // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
     private void deleteObject(String object_key, Integer target) {
+        if (target == -1) {
+            // [本地] 删除文件
+            uploadUtil.delete(object_key);
+        }
         if (target == 1) {
             // [腾讯云] 删除对象
             tencentCosService.deleteObject(object_key);

+ 108 - 0
src/main/java/com/backendsys/modules/upload/utils/UploadUtil.java

@@ -0,0 +1,108 @@
+package com.backendsys.modules.upload.utils;
+
+import com.backendsys.exception.CustException;
+import com.backendsys.modules.common.utils.CommonUtil;
+import com.backendsys.modules.upload.entity.SysFileResult;
+import com.backendsys.utils.response.Result;
+import com.backendsys.utils.response.ResultEnum;
+import net.coobird.thumbnailator.Thumbnails;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Component
+public class UploadUtil {
+
+    @Value("${file.upload.directory}") // 配置保存文件的目录
+    private String FILE_UPLOAD_DIRECTORY;
+
+    @Value("${file.upload.url-prefix}") // 指定文件访问的 URL前缀
+    private String FILE_UPLOAD_URL_PREFIX;
+
+    public SysFileResult put(MultipartFile multipartFile) {
+
+        try {
+            // 创建保存文件的目录,如果不存在
+            File directory = new File(FILE_UPLOAD_DIRECTORY);
+            if (!directory.exists()) { directory.mkdirs(); }
+
+            // 获取文件原始名
+            String originalFileName = multipartFile.getOriginalFilename();
+            // 获取后缀名
+            String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
+
+            // 生成新的文件名 (UUID)
+            String newFileName = CommonUtil.generateUUIDFilename(multipartFile);
+            String newFileNameThumb = newFileName.replaceAll(suffix, "-thumb" + suffix);
+
+            // 按日期作为文件目录
+            String pathDir = new SimpleDateFormat("yyyyMMdd").format(new Date());
+            String dateDir = pathDir + File.separator;
+
+            File dateDirectory = new File(FILE_UPLOAD_DIRECTORY + dateDir);
+            if (!dateDirectory.exists()) { dateDirectory.mkdirs(); }
+
+            // 确定文件保存的路径
+            String sourceFilePath = FILE_UPLOAD_DIRECTORY + dateDir + newFileName;
+//            String sourceFilePathThumb = FILE_UPLOAD_DIRECTORY + dateDir + newFileNameThumb;
+
+            System.out.println(sourceFilePath);
+//            System.out.println(sourceFilePathThumb);
+
+            File sourceFile = new File(sourceFilePath);
+//            File sourceFileThumb = new File(sourceFilePathThumb);
+
+            // 保存文件到本地
+            multipartFile.transferTo(sourceFile);
+            
+            // 创建缩略图
+            // .scale(0.5)  // 缩放
+            // .outputFormat("png") // 类型
+            // .sourceRegion(Positions.TOP_RIGHT, 1800, 1800) // 裁剪
+            // .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("watermark.png")), 0.5f)  // 添加水印
+            // .outputQuality(0.2) // 质量
+            
+            // 文件批量操作
+            // Thumbnails.of(new File("/images/202401/").listFiles()).size(400, 400).toFiles(Rename.PREFIX_DOT_THUMBNAIL);
+            
+//            Thumbnails.of(sourceFilePath).size(200, 200).toFile(sourceFilePathThumb);
+
+
+            // 返回文件的路径 (绝对路径)
+            String fileUrl = FILE_UPLOAD_URL_PREFIX + pathDir + "/" + newFileName;
+//            String fileThumbUrl = FILE_UPLOAD_URL_PREFIX + pathDir + "/" + newFileNameThumb;
+
+            // 自定义返回结果实体
+            SysFileResult result = new SysFileResult();
+            result.setDomain(fileUrl);
+            result.setKey(sourceFilePath);
+            return result;
+
+        } catch (IOException e) {
+            throw new CustException(e.getMessage());
+        }
+
+    }
+
+    public void delete(String object_key) {
+        // 根据传入的文件路径创建 File 对象
+        File fileToDelete = new File(object_key);
+        // 检查文件是否存在
+        if (fileToDelete.exists()) {
+            // 删除文件
+            if (fileToDelete.delete()) {
+                System.out.println("本地文件删除成功 (" + object_key + ")");
+            }
+        } else {
+            System.out.println("本地文件不存在,删除失败 (" + object_key + ")");
+        }
+    }
+
+}

+ 3 - 1
src/main/resources/application-local.yml

@@ -61,10 +61,12 @@ springdoc:
       paths-to-match: '/api/v2/system/**'
       packages-to-scan: com.backendsys.modules.system
 
-
+# 本地上传
 file:
   upload:
 
+    # (Ubuntu)
+    # directory: /home/www/project/BackendSys/uploads/
     # (Windows)
     directory: D://CodeJava//QuickLaunchSpring//BackendSys//uploads//
     # (MacOS)

+ 1 - 2
src/main/resources/application-prod.yml

@@ -61,13 +61,12 @@ springdoc:
 #      paths-to-match: '/api/v2/system/**'
 #      packages-to-scan: com.backendsys.modules.system
 
-
+# 本地上传
 file:
   upload:
 
     # (Ubuntu)
     directory: /home/www/project/BackendSys/uploads/
-
     # (Windows)
     # directory: D://CodeJava//QuickLaunchSpring//BackendSys//uploads//
     # (MacOS)