Ver código fonte

refactor(cms): 重构百度编辑器上传逻辑

- 重写 BaseAction 中的 exec 方法,优化编辑器上传逻辑
- 新增 uploadEditorScrawlFile、catchImage 等方法,处理不同类型的编辑器上传
- 改进 EditorAction 和 EditorFileVerifyAop 中的上传逻辑
- 更新前端模板,移除冗余代码
msgroup 1 mês atrás
pai
commit
9ea714d2ba

+ 1 - 26
src/main/java/net/mingsoft/MSApplication.java

@@ -22,40 +22,15 @@
 package net.mingsoft;
 
 import org.mybatis.spring.annotation.MapperScan;
-import org.springframework.boot.Banner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.servlet.ServletComponentScan;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.env.Environment;
-
-import java.util.logging.Logger;
 
 @SpringBootApplication(scanBasePackages = {"net.mingsoft"})
 @MapperScan(basePackages={"**.dao","com.baomidou.**.mapper"})
 @ServletComponentScan(basePackages = {"net.mingsoft"})
 public class MSApplication {
 	public static void main(String[] args) {
-		SpringApplication springApplication = new SpringApplication(MSApplication.class);
-		springApplication.setBannerMode(Banner.Mode.OFF);
-		ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
-		Environment env = configurableApplicationContext.getEnvironment();
-		System.out.printf(
-				"\n" +
-						"\033[1;36m" + // 青色加粗
-						"╔══════════════════════════════════════════════════════════╗\n" +
-						"║ \033[1;33m🚀 MCms Application Started Successfully! \033[1;36m               ║\n" +
-						"╠══════════════════════════════════════════════════════════╣\n" +
-						"║ \033[0;32m➜ Manager URL: \033[0;33mhttp://localhost:%s\033[1;36m%s\033[1;36m/login.do         ║\n" +
-						"║ \033[0;32m➜ Front URL: \033[0;33mhttp://localhost:%s/\033[1;36m                      ║\n" +
-						"║ \033[0;32m➜ Activate Profiles: \033[0;35m%s\033[1;36m                                 ║\n" +
-						"╚══════════════════════════════════════════════════════════╝" +
-						"\033[0m", // 重置颜色
-				env.getProperty("server.port"),
-				env.getProperty("ms.manager.path"),
-				env.getProperty("server.port"),
-				String.join(", ", env.getActiveProfiles())
-		);
+		SpringApplication.run(MSApplication.class, args);
 	}
 }
-

+ 206 - 118
src/main/java/net/mingsoft/cms/action/BaseAction.java

@@ -7,10 +7,10 @@
  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  * the Software, and to permit persons to whom the Software is furnished to do so,
  * subject to the following conditions:
-
+ * <p>
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
-
+ * <p>
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
@@ -25,18 +25,11 @@ package net.mingsoft.cms.action;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.io.file.FileNameUtil;
-import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.hutool.json.JSONArray;
-import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
-import com.baidu.ueditor.define.BaseState;
-import com.baidu.ueditor.define.State;
-import io.github.xcodeding.UeditorActionEnter;
 import jakarta.servlet.http.HttpServletRequest;
-import net.mingsoft.base.constant.Const;
 import net.mingsoft.base.entity.ResultData;
-import net.mingsoft.base.exception.BusinessException;
 import net.mingsoft.base.util.BundleUtil;
 import net.mingsoft.basic.action.BaseFileAction;
 import net.mingsoft.basic.bean.UploadConfigBean;
@@ -44,14 +37,16 @@ import net.mingsoft.basic.entity.AppEntity;
 import net.mingsoft.basic.service.IUploadBaseService;
 import net.mingsoft.basic.util.BasicUtil;
 import net.mingsoft.basic.util.SpringUtil;
-import net.mingsoft.config.MSProperties;
+import net.mingsoft.cms.bean.EditorStateBean;
 import net.mingsoft.mdiy.util.ConfigUtil;
+import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.binary.Hex;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.*;
 
 /**
@@ -61,20 +56,22 @@ import java.util.*;
 public class BaseAction extends BaseFileAction {
     /**
      * 读取国际化资源文件(没有占位符号的),优先模块对应的资源文件,如果模块资源文件找不到就会优先基础层
+     *
      * @param key 国际化文件key
      * @return 国际化字符串
      */
     protected String getResString(String key) {
-        return this.getResString(key,"");
+        return this.getResString(key, "");
     }
 
     /**
      * 读取国际化资源文件,优先模块对应的资源文件,如果模块资源文件找不到就会优先基础层
-     * @param key 国际化文件key
+     *
+     * @param key    国际化文件key
      * @param params 拼接值
      * @return 国际化字符串
      */
-    protected String getResString(String key,String... params) {
+    protected String getResString(String key, String... params) {
         // TODO Auto-generated method stub
         String str = "";
         try {
@@ -84,7 +81,7 @@ public class BaseAction extends BaseFileAction {
                 str = str.replace("{" + i + "}", params[i]);
             }
         } catch (MissingResourceException e) {
-            str = BundleUtil.getString(net.mingsoft.cms.constant.Const.RESOURCES, key,params);
+            str = BundleUtil.getString(net.mingsoft.cms.constant.Const.RESOURCES, key, params);
         }
 
         return str;
@@ -93,24 +90,14 @@ public class BaseAction extends BaseFileAction {
 
     /**
      * 抽取百度编辑器执行公共部分
-     * @param request 请求
-     * @param upfile 文件
+     *
+     * @param request           请求
+     * @param upfile            文件
      * @param execFileConfigMap 上传大小配置Map 应包含 imageMaxSize、videoMaxSize、fileMaxSize
-     * @param version 编辑器版本
+     * @param version           编辑器版本
      * @return 执行结果响应
      */
-    public String exec(HttpServletRequest request, MultipartFile upfile, Map execFileConfigMap, String version){
-        String uploadFolderPath = MSProperties.upload.path;
-        String uploadMapping = MSProperties.upload.mapping;
-
-        Map uploadConfig = ConfigUtil.getMap("文件上传配置");
-        if (MapUtil.isNotEmpty(uploadConfig)){
-            uploadFolderPath = uploadConfig.get("uploadPath").toString();
-            uploadMapping = uploadConfig.get("uploadMapping").toString();
-        }
-
-        // 获取真实完整上传路径
-        String rootPath = FileUtil.isAbsolutePath(uploadFolderPath) ? uploadFolderPath : BasicUtil.getRealPath(uploadFolderPath);
+    public String exec(HttpServletRequest request, MultipartFile upfile, Map execFileConfigMap, String version) {
 
         Map execConfigMap = new HashMap();
         execConfigMap.putAll(execFileConfigMap);
@@ -124,8 +111,6 @@ public class BaseAction extends BaseFileAction {
 
         String filePathFormat = uploadPath.concat("{time}");
 
-
-
         execConfigMap.put("imagePathFormat", filePathFormat);
         execConfigMap.put("filePathFormat", filePathFormat);
         execConfigMap.put("videoPathFormat", filePathFormat);
@@ -133,100 +118,203 @@ public class BaseAction extends BaseFileAction {
         execConfigMap.put("scrawlPathFormat", filePathFormat);
         execConfigMap.put("snapscreenPathFormat", filePathFormat);
 
-        String execConfig = JSONUtil.toJsonStr(execConfigMap);
+        String action = BasicUtil.getString("action");
+        // 判断当前编辑器什么操作
+        switch (action) {
+            case "uploadscrawl":
+                // 上传涂鸦文件
+                return uploadEditorScrawlFile(request, uploadPath);
+            case "uploadimage":
+            case "uploadvideo":
+            case "uploadfile":
+                // 上传文件
+                return uploadEditorFile(uploadPath, upfile);
+            case "catchimage":
+                // 抓取网络图片到本地
+                return catchImage(request, uploadPath);
+            default:
+                // 获取编辑器配置
+                return getEditorConfig(execConfigMap, BasicUtil.getRealPath(StrUtil.format("static/plugins/ueditor/{}/config.json", version)));
+        }
+    }
+
+    /**
+     * 上传编辑器涂鸦图片
+     *
+     * @param uploadPath 上传路径
+     * @param request    HttpServletRequest
+     * @return
+     */
+    private String uploadEditorScrawlFile(HttpServletRequest request, String uploadPath){
+        String base64 = request.getParameter("upfile");
+        byte[] bytes = Base64.decodeBase64(base64);
+        if (bytes == null) {
+            return new EditorStateBean(false, "上传涂鸦图片失败").toString();
+        }
+        // 尝试从流中获取后缀地址
+        MultipartFile file = net.mingsoft.basic.util.FileUtil.bytesToMultipartFile(bytes, "png");
+
+        if (ObjectUtil.isNull(file)) {
+            return new EditorStateBean(false, "上传涂鸦图片失败").toString();
+        }
+
+        UploadConfigBean bean = new UploadConfigBean(uploadPath, file, null, true);
+        bean.setFileSize(bytes.length);
+        bean.setFileName(file.getName());
+        EditorStateBean state = new EditorStateBean();
+
+        try {
+            state = this.uploadFile(bean);
+        } catch (IOException e) {
+            LOG.debug("上传编辑器涂鸦文件失败");
+            return new EditorStateBean(false, "上传涂鸦文件失败").toString();
+        }
+
+        return state.toString();
+    }
+
+    /**
+     * 抓取远程图片保存到本地
+     *
+     * @param request    HttpServletRequest
+     * @param uploadPath 上传路径
+     * @return
+     */
+    private String catchImage(HttpServletRequest request, String uploadPath) {
+        // 获取图片地址
+        String[] remotes = request.getParameterValues("source[]");
+        EditorStateBean multiState = new EditorStateBean(true);
+        List<EditorStateBean> states = new ArrayList<>();
+        for (String remote : remotes) {
+            if (StringUtils.isBlank(remote)) {
+                continue;
+            }
+
+            // 根据文件后缀当默认值,防止出现某些文件通过字节流获取后缀为空的情况
+            String suffix = FileNameUtil.getSuffix(remote);
+            suffix = StrUtil.isBlank(suffix) ? "png" : suffix;
+
+            // 转成multipartFile对象方便组装成上传bean
+            MultipartFile file = net.mingsoft.basic.util.FileUtil.remoteUrlToMultipartFile(remote, suffix);
+            if (file == null) {
+                continue;
+            }
+
+            UploadConfigBean bean = new UploadConfigBean(uploadPath, file, null, true);
+            bean.setFileSize(file.getSize());
+            bean.setFileName(file.getName());
 
-        // 如果upfile不为空,说明此处操作时上传附件操作
-        if (upfile != null){
-            State state = null;
-            String upFileMainName = FileNameUtil.mainName(upfile.getOriginalFilename());
-            if (StringUtils.isBlank(upFileMainName)){
-                return new BaseState(false, getResString("err.error",getResString("file.name"))).toJSONString();
+            EditorStateBean state = new EditorStateBean();
+            try {
+                state = this.uploadFile(bean);
+            } catch (IOException e) {
+                LOG.debug("抓取图片失败");
+                state = new EditorStateBean(false, "抓取图片失败");
             }
-            // 组装uploadConfigBean上传使用
-            UploadConfigBean bean = new UploadConfigBean(uploadPath, upfile, null, true);
-            bean.setFileSize(upfile.getSize());
-            bean.setFileName(upfile.getOriginalFilename());
+            // 这里还需要把源文件地址添加到结果中
+            if (state.isSuccess()) {
+                state.put("state", "SUCCESS");
+                state.put("source", remote);
+            }
+            states.add(state);
+        }
+        multiState.put("list", states);
+        return multiState.toString();
+    }
+
+
+    /**
+     * 上传编辑器文件
+     *
+     * @param uploadPath 上传路径
+     * @param upfile     文件
+     * @return
+     */
+    private String uploadEditorFile(String uploadPath, MultipartFile upfile) {
+        String upFileMainName = FileNameUtil.mainName(upfile.getOriginalFilename());
+        if (StringUtils.isBlank(upFileMainName)) {
+            return new EditorStateBean(false, getResString("err.error", getResString("file.name"))).toString();
+        }
+        // 组装uploadConfigBean上传使用
+        UploadConfigBean bean = new UploadConfigBean(uploadPath, upfile, null, true);
+        bean.setFileSize(upfile.getSize());
+        bean.setFileName(upfile.getOriginalFilename());
+        try {
+            return this.uploadFile(bean).toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return new EditorStateBean(false, "上传图片失败").toString();
+        }
+    }
+
+
+    /**
+     * 获取编辑器配置
+     * @param configJson 配置json
+     * @param jsonPath json文件路径
+     * @return
+     */
+    private String getEditorConfig(Map configJson, String jsonPath) {
+        // 读取本地json文件
+        String _configJson = FileUtil.readString(jsonPath, StandardCharsets.UTF_8);
+        // 过滤中文字符串
+        _configJson = _configJson.replaceAll("/\\*[\\s\\S]*?\\*/", "");
+        Map jsonMap = JSONUtil.toBean(_configJson, Map.class);
+        // 拿自定义数据替换json中某些数据
+        jsonMap.putAll(configJson);
+        return JSONUtil.toJsonStr(jsonMap);
+    }
+
+    /**
+     * 上传文件到本地并且组装成百度编辑器格式返回
+     * @param bean 上传配置
+     * @return 上传结果
+     * @throws IOException 抛出异常
+     */
+    private EditorStateBean uploadFile(UploadConfigBean bean) throws IOException {
+        // 判断是依赖ms-file插件
+        String type = ConfigUtil.getString("存储设置", "storeSelect");
+        IUploadBaseService uploadBaseService = null;
+        EditorStateBean state = new EditorStateBean(true);
+        // 单个文件上传计算下各个参数值避免重复上传
+        if (StringUtils.isBlank(bean.getFileIdentifier())) {
             try {
-                // 判断是依赖ms-file插件
-                String type = ConfigUtil.getString("存储设置", "storeSelect");
-                IUploadBaseService uploadBaseService = null;
-                // 单个文件上传计算下各个参数值避免重复上传
-                if (StringUtils.isBlank(bean.getFileIdentifier())){
-                    try {
-                        bean.setFileIdentifier(String.valueOf(Hex.encodeHex(MessageDigest.getInstance("MD5").digest(bean.getFile().getBytes()))));
-                    } catch (NoSuchAlgorithmException e) {
-                        e.printStackTrace();
-                    }
-                }
-                if (StringUtils.isNotBlank(type)) {
-                    //ms-file 插件启用
-                    uploadBaseService = (IUploadBaseService) SpringUtil.getBean(type);
-                }
-                ResultData resultData = null;
-                // 根据不同类型决定上传逻辑
-                if (uploadBaseService != null) {
-                    resultData = uploadBaseService.upload(bean);
-                } else {
-                    resultData = this.upload(bean);
-                }
-                if (resultData.isSuccess()) {
-                    // 组装百度编辑器格式
-                    state = new BaseState(true);
-                    state.putInfo("original", upfile.getOriginalFilename());
-                    state.putInfo("size", upfile.getSize());
-                    state.putInfo("title", FileNameUtil.getName(resultData.getData(String.class)));
-                    state.putInfo("type", "." + FileNameUtil.getSuffix(upfile.getOriginalFilename()));
-                    // 由于百度编辑器不是正常的uploadConfigBean,是无法正常通过getFileIdentifier获取到文件标识的,所以在这里做一个容错处理
-                    state.putInfo("fileIdentifier", bean.getFileIdentifier());
-                    // 判断是否有contentPath
-                    String contextPath = BasicUtil.getContextPath();
-                    String filePath = resultData.getData(String.class);
-                    if (StringUtils.isNotBlank(filePath) && !filePath.startsWith("http")) {
-                        filePath = (contextPath.equals("/") ? "" : contextPath) + filePath;
-                    }
-                    state.putInfo("url", filePath);
-                } else {
-                    state = new BaseState(false, resultData.getMsg());
-                }
-                return state.toJSONString();
+                bean.setFileIdentifier(String.valueOf(Hex.encodeHex(MessageDigest.getInstance("MD5").digest(bean.getFile().getBytes()))));
             } catch (Exception e) {
                 e.printStackTrace();
-                return new BaseState(false, 500).toJSONString();
             }
         }
-
-        //过滤非法上传路径
-        if (execConfig != null && (execConfig.contains("../") || execConfig.contains("..\\"))) {
-            return new BaseState(false, BundleUtil.getString(Const.RESOURCES,"err.error",BundleUtil.getString(net.mingsoft.basic.constant.Const.RESOURCES,"file.path"))).toJSONString();
+        if (StringUtils.isNotBlank(type)) {
+            //ms-file 插件启用
+            uploadBaseService = (IUploadBaseService) SpringUtil.getBean(type);
         }
-        UeditorActionEnter actionEnter = new UeditorActionEnter(upfile,request, rootPath, execConfig, BasicUtil.getRealPath(""), BasicUtil.getRealPath(StrUtil.format("static/plugins/ueditor/{}/config.json",version)));
-        String result = actionEnter.exec();
-        // 获取请求类型
-        String action = BasicUtil.getString("action");
-        Map jsonMap = JSONUtil.toBean(result,Map.class);
-        // 这里会有图片复制粘贴情况
-        if ("catchimage".equalsIgnoreCase(action) && jsonMap.get("state").equals("SUCCESS")) {
-            // 处理绝对路径和项目名情况
-            JSONArray fileUrls = JSONUtil.parseArray(MapUtil.getStr(jsonMap, "list", "[]"));
-            String url = "";
-            List<JSONObject> list = new ArrayList<>();
-            // 循环上传文件,判断是否有项目名和绝对路径
-            for (Object fileUrl : fileUrls) {
-                JSONObject jsonObject = (JSONObject) fileUrl;
-                url = jsonObject.getStr("url");
-                // 判断是否有contentPath
-                String contextPath = BasicUtil.getContextPath();
-                if (StringUtils.isNotBlank(url) && !url.startsWith("http")) {
-                    url = "/".concat(uploadMapping.replaceAll("/([\\s\\S]*)/\\*\\*", "$1")).concat(url + "");
-                    url = (contextPath.equals("/") ? "" : contextPath) + url;
-                }
-                jsonObject.set("url", url);
-                list.add(jsonObject);
+        ResultData resultData = null;
+        // 根据不同类型决定上传逻辑
+        if (uploadBaseService != null) {
+            resultData = uploadBaseService.upload(bean);
+        } else {
+            resultData = this.upload(bean);
+        }
+        if (resultData.isSuccess()) {
+            // 组装百度编辑器格式
+            state = new EditorStateBean(true);
+            state.put("original", bean.getFileName());
+            state.put("size", bean.getFileSize());
+            state.put("title", FileNameUtil.getName(resultData.getData(String.class)));
+            state.put("type", "." + FileNameUtil.getSuffix(bean.getFileName()));
+            // 由于百度编辑器不是正常的uploadConfigBean,是无法正常通过getFileIdentifier获取到文件标识的,所以在这里做一个容错处理
+            state.put("fileIdentifier", bean.getFileIdentifier());
+            // 判断是否有contentPath
+            String contextPath = BasicUtil.getContextPath();
+            String filePath = resultData.getData(String.class);
+            if (StringUtils.isNotBlank(filePath) && !filePath.startsWith("http")) {
+                filePath = (contextPath.equals("/") ? "" : contextPath) + filePath;
             }
-            jsonMap.put("list", list);
+            state.put("url", filePath);
+        } else {
+            state = new EditorStateBean(false, resultData.getMsg());
         }
-        return JSONUtil.toJsonStr(jsonMap);
-
-
+        return state;
     }
+
 }

+ 11 - 0
src/main/java/net/mingsoft/cms/action/EditorAction.java

@@ -1,6 +1,7 @@
 package net.mingsoft.cms.action;
 
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
 import io.swagger.v3.oas.annotations.Hidden;
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletRequest;
@@ -14,6 +15,7 @@ import org.springframework.web.multipart.MultipartFile;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 百度编辑器后台上传<br>
@@ -48,6 +50,15 @@ public class EditorAction extends BaseAction{
             map.put("imageMaxSize", MapUtil.getLong(uploadConfig,"imageSize") * 1000);
             map.put("videoMaxSize", MapUtil.getLong(uploadConfig,"videoSize") * 1000);
             map.put("fileMaxSize", MapUtil.getLong(uploadConfig,"fileSize") * 1000);
+
+            // 不做空判断,hutool中的StrUtil.split已做处理,返回一个空数组
+            String imageType = MapUtil.getStr(uploadConfig, "imageType");
+            String videoType = MapUtil.getStr(uploadConfig, "videoType");
+            String fileType = MapUtil.getStr(uploadConfig, "fileType");
+            map.put("imageAllowFiles", StrUtil.split(imageType, ",", true,  true).stream().map(str -> "." + str).collect(Collectors.toList()));
+            map.put("videoAllowFiles", StrUtil.split(videoType, ",", true,  true).stream().map(str -> "." + str).collect(Collectors.toList()));
+            map.put("fileAllowFiles", StrUtil.split(fileType, ",", true,  true).stream().map(str -> "." + str).collect(Collectors.toList()));
+
         }else {
             // 控制大小
             map.put("imageMaxSize", maxFileSize * 1000);

+ 5 - 4
src/main/java/net/mingsoft/cms/action/GeneraterAction.java

@@ -37,6 +37,8 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameters;
 import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import net.mingsoft.base.entity.ResultData;
 import net.mingsoft.basic.annotation.LogAnn;
 import net.mingsoft.basic.bean.EUListBean;
@@ -63,8 +65,6 @@ import org.springframework.stereotype.Controller;
 import org.springframework.ui.ModelMap;
 import org.springframework.web.bind.annotation.*;
 
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -352,10 +352,11 @@ public class GeneraterAction extends BaseAction {
      * @return
      */
     @Operation(summary =  "预览主页接口")
-    @RequestMapping(value = "/{position}/viewIndex", method = {RequestMethod.GET, RequestMethod.POST})
-    public String viewIndex(HttpServletRequest request, @PathVariable String position, HttpServletResponse response) {
+    @RequestMapping(value = "/viewIndex", method = {RequestMethod.GET, RequestMethod.POST})
+    public String viewIndex(HttpServletRequest request,  HttpServletResponse response) {
         AppEntity app = BasicUtil.getApp();
         // 组织主页预览地址
+        String position = request.getParameter("position");
         String indexPosition = app.getAppHostUrl() + htmlDir + File.separator + app.getAppDir()
                 + File.separator + position + ParserUtil.HTML_SUFFIX;
         return "redirect:" + indexPosition;

+ 13 - 4
src/main/java/net/mingsoft/cms/action/web/EditorAction.java

@@ -27,6 +27,7 @@
 package net.mingsoft.cms.action.web;
 
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import io.swagger.v3.oas.annotations.Hidden;
 import jakarta.annotation.Resource;
@@ -42,6 +43,7 @@ import org.springframework.web.multipart.MultipartFile;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 百度编辑器上传<br>
@@ -67,19 +69,26 @@ public class EditorAction extends BaseAction {
         boolean enableWeb = MSProperties.upload.enableWeb;
 
         Map uploadConfig = ConfigUtil.getMap("文件上传配置");
+        Map<String, Object> map = new HashMap<>();
         long maxFileSize = msProperties.getUpload().getMultipart().getMaxFileSize() * 1000;
         // 兼容其他版本的上传配置
         if (MapUtil.isNotEmpty(uploadConfig)){
-            enableWeb = Boolean.parseBoolean(String.valueOf(uploadConfig.get("uploadEnable")));
+            enableWeb = MapUtil.getBool(uploadConfig, "enableWeb");
             maxFileSize = MapUtil.getLong(uploadConfig,"webFileSize") * 1000;
+            // 由于我们webFileType是一个大集合,不像管理员端那样细分,所以这边改成
+            // 不做空判断,hutool中的StrUtil.split已做处理,返回一个空数组
+            String imageType = MapUtil.getStr(uploadConfig, "imageType");
+            String videoType = MapUtil.getStr(uploadConfig, "videoType");
+            String fileType = MapUtil.getStr(uploadConfig, "fileType");
+            map.put("imageAllowFiles", StrUtil.split(imageType, ",", true,  true).stream().map(str -> "." + str).collect(Collectors.toList()));
+            map.put("videoAllowFiles", StrUtil.split(videoType, ",", true,  true).stream().map(str -> "." + str).collect(Collectors.toList()));
+            map.put("fileAllowFiles", StrUtil.split(fileType, ",", true,  true).stream().map(str -> "." + str).collect(Collectors.toList()));
+
         }
         if (!enableWeb) {
-            HashMap<String, String> map = new HashMap<>();
             map.put("state","配置不允许前端上传文件");
             return JSONUtil.toJsonStr(map);
         }
-        Map<String, Object> map = new HashMap<>();
-
         // 控制大小
         map.put("imageMaxSize", maxFileSize);
         map.put("videoMaxSize", maxFileSize);

+ 103 - 31
src/main/java/net/mingsoft/cms/aop/EditorFileVerifyAop.java

@@ -21,10 +21,17 @@
 
 package net.mingsoft.cms.aop;
 
-import com.baidu.ueditor.define.BaseState;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import net.mingsoft.base.entity.ResultData;
 import net.mingsoft.basic.aop.FileVerifyAop;
 import net.mingsoft.basic.bean.UploadConfigBean;
+import net.mingsoft.basic.util.BasicUtil;
+import net.mingsoft.basic.util.FileUtil;
+import net.mingsoft.basic.util.SpringUtil;
+import net.mingsoft.cms.bean.EditorStateBean;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
@@ -32,6 +39,9 @@ import org.aspectj.lang.annotation.Pointcut;
 import org.springframework.stereotype.Component;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * @author 铭软开发团队
  * @ClassName: EditorFileVerifyAop
@@ -56,25 +66,26 @@ public class EditorFileVerifyAop extends FileVerifyAop {
      */
     @Around("execution(* net.mingsoft.cms.action.EditorAction.editor(..)) ")
     public Object uploadAop(ProceedingJoinPoint joinPoint) throws Throwable {
-        Object[] args = joinPoint.getArgs();
-        MultipartFile file = null;
-        // TODO: 2025/6/12 通过getType无法获取file文件信息,所以改成这个方法获取文件信息
-        for (Object arg : args) {
-            if (arg instanceof MultipartFile) {
-                file = (MultipartFile) arg;
-            }
-        }
-        // 如果没有文件,可能是获取配置文件时,直接返回
-        if (file == null) {
+        // 1. 获取请求操作
+        String action = BasicUtil.getString("action");
+        // 获取配置操作直接返回
+        if (StrUtil.isBlank(action) || "config".equals(action)) {
             return joinPoint.proceed();
         }
+
+        List<MultipartFile> files = this.getFiles(joinPoint);
+
         UploadConfigBean bean = new UploadConfigBean();
-        bean.setFile(file);
-        ResultData resultData = prepareUpload(bean,false);
-        if (resultData.isSuccess()) {
-            return joinPoint.proceed();
+        // 会有抓取批量操作
+        for (MultipartFile multipartFile : files) {
+            bean = new UploadConfigBean();
+            bean.setFile(multipartFile);
+            ResultData resultData = prepareUpload(bean, false);
+            if (!resultData.isSuccess()) {
+                return new EditorStateBean(false, resultData.getMsg()).toString();
+            }
         }
-        return new BaseState(false, resultData.getMsg()).toString();
+        return joinPoint.proceed();
     }
 
     /**
@@ -85,25 +96,86 @@ public class EditorFileVerifyAop extends FileVerifyAop {
      */
     @Around("execution(* net.mingsoft.cms.action.web.EditorAction.editor(..))")
     public Object webUploadAop(ProceedingJoinPoint joinPoint) throws Throwable {
-        Object[] args = joinPoint.getArgs();
-        MultipartFile file = null;
-        // TODO: 2025/6/12 通过getType无法获取file文件信息,所以改成这个方法获取文件信息
-        for (Object arg : args) {
-            if (arg instanceof MultipartFile) {
-                file = (MultipartFile) arg;
-            }
-        }
-        // 如果没有文件,可能是获取配置文件时,直接返回
-        if (file == null) {
+        // 1. 获取请求操作
+        String action = BasicUtil.getString("action");
+        // 获取配置操作直接返回
+        if (StrUtil.isBlank(action) || "config".equals(action)) {
             return joinPoint.proceed();
         }
+
+        List<MultipartFile> files = this.getFiles(joinPoint);
+
         UploadConfigBean bean = new UploadConfigBean();
-        bean.setFile(file);
-        ResultData resultData = prepareUpload(bean,true);
-        if (resultData.isSuccess()) {
-            return joinPoint.proceed();
+        // 会有抓取批量操作
+        for (MultipartFile multipartFile : files) {
+            bean = new UploadConfigBean();
+            bean.setFile(multipartFile);
+            ResultData resultData = prepareUpload(bean, true);
+            if (!resultData.isSuccess()) {
+                return new EditorStateBean(false, resultData.getMsg()).toString();
+            }
+        }
+        return joinPoint.proceed();
+    }
+
+    /**
+     * 获取上传的文件数组,可能存在多个
+     * @param pjp
+     * @return
+     * @throws Exception
+     */
+    private List<MultipartFile> getFiles(ProceedingJoinPoint pjp) throws Exception{
+        // 1. 获取请求操作
+        String action = BasicUtil.getString("action");
+
+        List<MultipartFile> files = new ArrayList<>();
+        MultipartFile file = null;
+        // 判断当前编辑器什么操作
+        switch (action) {
+            case "uploadscrawl":
+                // 上传涂鸦文件
+                String base64 = SpringUtil.getRequest().getParameter("upfile");
+                byte[] bytes = Base64.decodeBase64(base64);
+                // 尝试从流中获取后缀地址
+
+                file = FileUtil.bytesToMultipartFile(bytes, "png");
+                if (ObjectUtil.isNull(file)) {
+                    files.add(file);
+                }
+                break;
+            case "uploadimage":
+            case "uploadvideo":
+            case "uploadfile":
+                // 上传文件
+                Object[] args = pjp.getArgs();
+                file = null;
+                // TODO: 2025/6/12 通过getType无法获取file文件信息,所以改成这个方法获取文件信息
+                for (Object arg : args) {
+                    if (arg instanceof MultipartFile) {
+                        file = (MultipartFile) arg;
+                    }
+                }
+                files.add(file);
+                break;
+            case "catchimage":
+                // 抓取网络图片到本地
+                // 获取图片地址
+                String[] remotes = SpringUtil.getRequest().getParameterValues("source[]");
+                for (String remote : remotes) {
+                    if (StringUtils.isBlank(remote)) {
+                        continue;
+                    }
+                    file = FileUtil.remoteUrlToMultipartFile(remote, "png");
+                    if (file == null) {
+                        continue;
+                    }
+                    files.add(file);
+                }
+                break;
+            default:
+                break;
         }
-        return new BaseState(false, resultData.getMsg()).toString();
+        return files;
     }
 
 }

+ 1 - 1
src/main/java/net/mingsoft/cms/dao/IContentDao.xml

@@ -122,7 +122,7 @@
 				<if test="contentDescription != null and contentDescription != ''">#{contentDescription},</if>
 				<if test="contentKeyword != null and contentKeyword != ''">#{contentKeyword},</if>
 				<if test="contentDetails != null and contentDetails != ''">#{contentDetails},</if>
-				<if test="contentUrl != null and contentUrl != ''">#{contentUrl},</if>
+				<if test="contentOutLink != null and contentOutLink != ''">#{contentOutLink},</if>
 				<if test="contentHit != null">#{contentHit},</if>
 				<if test="createBy &gt; 0">#{createBy},</if>
 				<if test="createDate != null">#{createDate},</if>

+ 1 - 0
src/main/java/net/mingsoft/cms/resources/resources_en_US.properties

@@ -2,6 +2,7 @@
 #Thu Nov 28 15:12:32 CST 2019
 category.img=Thumbnail
 appid=The application id of article management
+content.id=Article id
 content.datetime=Publishing time
 category.manager.id=Posting user id
 category.title=Column management name

+ 1 - 0
src/main/java/net/mingsoft/cms/resources/resources_zh_CN.properties

@@ -2,6 +2,7 @@
 #Thu Nov 28 15:12:32 CST 2019
 category.img=\u7F29\u7565\u56FE
 appid=\u6587\u7AE0\u7BA1\u7406\u7684\u5E94\u7528id
+content.id=\u6587\u7ae0\u7f16\u53f7
 content.datetime=\u53D1\u5E03\u65F6\u95F4
 category.manager.id=\u53D1\u5E03\u7528\u6237id
 category.title=\u680F\u76EE\u7BA1\u7406\u540D\u79F0

+ 1 - 13
src/main/webapp/WEB-INF/manager/cms/content/form.ftl

@@ -1,4 +1,3 @@
-<#include "mdiy/components/ms-dict.ftl">
 <template type="text/x-template" id="content-form">
     <div id="form" v-cloak>
         <el-header class="ms-header ms-tr" height="50px" >
@@ -23,7 +22,7 @@
         </el-header>
         <el-main class="ms-container" style="position:relative;">
             <el-scrollbar class="ms-scrollbar" style="height: 100%;">
-                <el-tabs v-model="activeName" style="height: calc(100% - 10px);">
+                <el-tabs v-model="activeName" style="height: calc(100% - 10px);" tab-position="top">
                     <el-tab-pane style="position:relative;" v-for="(item, index) in editableTabs" :key="index"
                                  :label="item.title" :name="item.name">
                         <el-form v-if="item.title=='文章编辑'" ref="form" :model="form" :rules="rules" label-width="120px"
@@ -303,15 +302,8 @@
         template: '#content-form',
         props:["categoryId","categoryType","id"],
         components:{
-            MsDict
         },
         data: function () {
-            var checkTags = function (rule, value, callback){
-                if (value.length > 5){
-                    return callback(new Error('文章标签最多选择5个'));
-                }
-                callback();
-            }
             return {
                 editorHidden:true,
                 saveDisabled: false,
@@ -433,10 +425,6 @@
                         "max":500,
                         "message":"描述长度必须为0-500"
                     }],
-                    // 文章标签
-                    contentTags: [{
-                        validator: checkTags, trigger: 'blur'
-                    }]
                 },
                 historyKey:"cms_content_history"
             };

+ 1 - 1
src/main/webapp/WEB-INF/manager/cms/generate/index.ftl

@@ -294,7 +294,7 @@
                     });
                     return;
                 }
-                window.open(ms.manager + "/cms/generate/" + this.position + "/viewIndex.do");
+                window.open(ms.manager + "/cms/generate/viewIndex.do?position="+this.position);
             },
             //更新栏目
             updateColumn: function () {

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/main/webapp/static/plugins/ms/3.0/ms.umd.js


Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff