浏览代码

预览文档前检查文件是否存在

陈长荣 2 月之前
父节点
当前提交
586b98414a

+ 10 - 6
pom.xml

@@ -33,11 +33,6 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-aop</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-bootstrap</artifactId>
-            <version>3.1.5</version>
-        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
@@ -45,7 +40,16 @@
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-actuator</artifactId>
-            <version>2.7.17</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+            <version>3.1.5</version>
         </dependency>
         <dependency>
             <groupId>com.baomidou</groupId>

+ 35 - 0
src/main/java/com/github/jfcloud/excel/editor/docdeal/bean/PreviewVo.java

@@ -0,0 +1,35 @@
+package com.github.jfcloud.excel.editor.docdeal.bean;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+
+@Data
+public class PreviewVo {
+
+    private String bucket;
+
+    private String title;
+
+    private String url;
+
+    private String name;
+
+    @NotEmpty(message = "用户id不能为空")
+    private String userId;
+
+    @NotEmpty(message = "用户名不能为空")
+    private String userName;
+
+    /**
+     * 是否可编辑
+     */
+    private boolean edit;
+
+    public void setUrl(String url) {
+        this.url = url;
+        this.name = url.substring(url.lastIndexOf('/') + 1);
+        bucket = url.replace("/admin/sys-file/", "");
+        bucket = bucket.substring(0, bucket.indexOf('/'));
+    }
+}

+ 0 - 11
src/main/java/com/github/jfcloud/excel/editor/docdeal/config/Crosconfig.java

@@ -1,8 +1,6 @@
 package com.github.jfcloud.excel.editor.docdeal.config;
 
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.web.client.RestTemplate;
 import org.springframework.web.servlet.config.annotation.CorsRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@@ -18,13 +16,4 @@ public class Crosconfig implements WebMvcConfigurer {
                 .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                 .maxAge(3600);
     }
-
-    /**
-     * 配置restTemplate工具类
-     * @return
-     */
-    @Bean
-    public RestTemplate restTemplate(){
-        return new RestTemplate();
-    }
 }

+ 53 - 75
src/main/java/com/github/jfcloud/excel/editor/docdeal/controller/FileController.java

@@ -1,5 +1,6 @@
 package com.github.jfcloud.excel.editor.docdeal.controller;
 
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.io.file.FileNameUtil;
 import cn.hutool.core.lang.Pair;
@@ -24,7 +25,6 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
-import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
@@ -38,8 +38,6 @@ import java.util.stream.Collectors;
 @Slf4j
 @Controller
 public class FileController {
-    @Autowired
-    RestTemplate restTemplate;
     @Autowired
     OssTemplate ossTemplate;
     @Autowired
@@ -108,73 +106,81 @@ public class FileController {
         }
         log.info("==> 下载文档: name={}, bucket={}", name, bucket);
 
-        long size = ossTemplate.getSize(bucket, name);
-
         try {
-            if (size < 0) {
+            S3Object s3Object = ossTemplate.getObject(bucket, name);
+            if (s3Object == null) {
                 log.error("文档不存在 name={}, bucket={}", name, bucket);
-                response.getWriter().println("Sorry, we cannot find the file, pls check the file name and try again. --" + System.currentTimeMillis());
+                response.getWriter().println(DateUtil.now() + " Sorry, we cannot find the file ( T_T )");
                 return;
             }
 
             response.setContentType("application/octet-stream");
+            response.setContentLength((int) s3Object.getObjectMetadata().getContentLength());
             response.setHeader("Content-Disposition", "attachment;filename*=utf8''" + URLEncodeUtil.encode(name));
-            response.setHeader("Content-Length", String.valueOf(size));
 
-            S3Object s3Object = ossTemplate.getObject(bucket, name);
             IoUtil.copy(s3Object.getObjectContent(), response.getOutputStream());
         } catch (IOException e) {
             log.error("文档下载失败 name={}, error={}", name, e.getMessage());
         }
     }
 
+    @GetMapping("/reviewExcel")
+    public String reviewExcel(PreviewVo vo, Model model) {
+        return reviewDocFile(vo, model);
+    }
+
     @GetMapping("/review")
-    public String reviewDocFile(@RequestParam(required = false) String bucket,
-                                @RequestParam(required = false) String title,
-                                @RequestParam(required = false) String url,
-                                @RequestParam(required = false) String name,
-                                @RequestParam String userId,
-                                @RequestParam String userName,
-                                Model model) {
-        if (StrUtil.isNotBlank(url)) {
-            url = url.replace("/admin/sys-file/", "");
-            bucket = url.substring(0, url.indexOf('/'));
-            name = url.substring(url.lastIndexOf('/') + 1);
+    public String reviewDocFile(PreviewVo vo, Model model) {
+        vo.setEdit(false);
+        log.info("==> 查看文档 vo: {}", vo);
+        if (!ossTemplate.exist(vo.getBucket(), vo.getName())) {
+            model.addAttribute("name", vo.getName());
+            return "/notFound";
         }
 
-        log.info("==> 预览文档 bucket: {}, name: {}", bucket, name);
-
-        Pair<Document, DocumentEditParam> pair = docPair(bucket, title, name, userId, userName);
-        Document document = pair.getKey();
-        model.addAttribute("document", document);
+        Pair<Document, DocumentEditParam> pair = docInfo(vo);
+        model.addAttribute("document", pair.getKey());
         model.addAttribute("documentEditParam", pair.getValue());
 
-        if (document.getFileType().equals("xls") || document.getFileType().equals("xlsx")) {
+        String suffix = FileNameUtil.getSuffix(vo.getName());
+        if (suffix.equals("xls") || suffix.equals("xlsx")) {
             return "/viewerExcel";
         }
         return "/viewer";
     }
 
-    private Pair<Document, DocumentEditParam> docPair(String bucket, String title, String name,
-                                                      String userId, String userName) {
+    @GetMapping("/edit")
+    public String editDocFile(PreviewVo vo, Model model) {
+        vo.setEdit(true);
+        log.info("==> 编辑文档 vo: {}", vo);
+        if (!ossTemplate.exist(vo.getBucket(), vo.getName())) {
+            model.addAttribute("name", vo.getName());
+            return "/notFound";
+        }
+        Pair<Document, DocumentEditParam> pair = docInfo(vo);
+        model.addAttribute("document", pair.getKey());
+        model.addAttribute("documentEditParam", pair.getValue());
+        return "/editor";
+    }
 
-        title = StrUtil.isBlank(title) ? name : title;
-        bucket = StrUtil.isBlank(bucket) ? ossProperties.getBucketName() : bucket;
-        String downloadUrl = String.format(DocumentConstants.OFFICE_API_DOC_FILE_BUCKET, serverUrl, name, bucket) + "&_t=" + System.currentTimeMillis();
+    private Pair<Document, DocumentEditParam> docInfo(PreviewVo vo) {
+        String title = StrUtil.blankToDefault(vo.getTitle(), vo.getName());
+        String bucket = StrUtil.blankToDefault(vo.getBucket(), ossProperties.getBucketName());
+        String downloadUrl = String.format(DocumentConstants.OFFICE_API_DOC_FILE_BUCKET, serverUrl, vo.getName(), bucket) + "&_t=" + System.currentTimeMillis();
 
         Document document = Document.builder()
                 .key(IdUtil.fastSimpleUUID())
                 .title(title)
-                .fileType(FileNameUtil.getSuffix(name))
+                .fileType(FileNameUtil.getSuffix(vo.getName()))
                 .url(downloadUrl)
                 .storage(bucket)
-                .len(ossTemplate.getSize(bucket, name))
+                .len(ossTemplate.getSize(bucket, vo.getName()))
                 .permissions(new JSONObject()
                         .fluentPut("chat", true)
                         .fluentPut("comment", true)
                         .fluentPut("copy", true)
                         .fluentPut("download", true)
-                        .fluentPut("edit", false)
+                        .fluentPut("edit", vo.isEdit())
                         .fluentPut("fillForms", false)
                         .fluentPut("modifyContentControl", true)
                         .fluentPut("modifyFilter", true)
@@ -188,43 +194,14 @@ public class FileController {
 
         //回调接口回传文档名称和存储桶
         DocumentEditParam param = DocumentEditParam.builder()
-                .callbackUrl(String.format(DocumentConstants.OFFICE_API_CALLBACK_BUCKET, serverUrl, name, bucket))
-                .user(new DocumentEditParam.UserBean(userId, userName))
+                .callbackUrl(String.format(DocumentConstants.OFFICE_API_CALLBACK_BUCKET, serverUrl, vo.getName(), bucket))
+                .user(new DocumentEditParam.UserBean(vo.getUserId(), vo.getUserName()))
                 .build();
         log.info("param: {}", JSON.toJSONString(param));
 
         return Pair.of(document, param);
     }
 
-    @GetMapping("/reviewExcel")
-    public String reviewExcel(@RequestParam(required = false) String bucket,
-                              @RequestParam(required = false) String title,
-                              @RequestParam(required = false) String url,
-                              @RequestParam(required = false) String name,
-                              String userId,
-                              String userName,
-                              Model model) {
-        return reviewDocFile(bucket, title, url, name, userName, userId, model);
-    }
-
-    @GetMapping("/edit")
-    public String editDocFile(@RequestParam(required = false) String bucket,
-                              @RequestParam(required = false) String title,
-                              @RequestParam String name,
-                              String userName,
-                              String userId,
-                              Model model) {
-        log.info("==> 编辑文档 bucket: {}, name: {}", bucket, name);
-
-        Pair<Document, DocumentEditParam> pair = docPair(bucket, title, name, userId, userName);
-        Document document = pair.getKey();
-        document.getPermissions().clear();
-        model.addAttribute("document", document);
-        model.addAttribute("documentEditParam", pair.getValue());
-        return "/editor";
-    }
-
-
     /**
      * Only Office 回调接口
      * <a href="https://api.onlyoffice.com/zh-CN/docs/docs-api/usage-api/callback-handler/">回调参数</a>
@@ -240,42 +217,43 @@ public class FileController {
                 .stream()
                 .map(DocumentEditCallback.ActionsBean::getUserid)
                 .collect(Collectors.joining(", "));
+        String msg = String.format("bucket=%s name=%s user=%s", bucket, name, userIds);
 
         try {
             switch (param.getStatus()) {
                 case 0:
-                    log.info("找不到具有对应key的文档 user={}", userIds);
+                    log.info("找不到具有对应key的文档 {}", msg);
                     break;
                 case 1:
-                    log.info("文档正在被编辑 user={}", userIds);
+                    log.info("文档正在被编辑 {}", msg);
                     break;
                 case 2:
                 case 6:
                     if (param.getStatus() == 2) {
-                        log.info("文档已准备好保存 user={}", userIds);
+                        log.info("文档已准备好保存 {}", msg);
                     } else {
-                        log.info("正在进行强制保存请求 user={}", userIds);
+                        log.info("正在进行强制保存请求 {}", msg);
                     }
 
                     //更新oss文件
                     byte[] bytes = HttpUtil.downloadBytes(param.getUrl());
                     ossTemplate.putObject(bucket, name, new ByteArrayInputStream(bytes));
-                    log.info("文档已保存,bucket={} name={}", bucket, name);
+                    log.info("文档已保存 {}", msg);
                     break;
                 case 3:
-                    log.info("文档保存时发生错误 user={}", userIds);
+                    log.warn("文档保存时发生错误 {}", msg);
                     break;
                 case 4:
-                    log.info("文档关闭时没有更改 user={}", userIds);
+                    log.info("文档关闭时没有更改 {}", msg);
                     break;
                 case 7:
-                    log.info("强制保存文档时发生错误 user={}", userIds);
+                    log.error("强制保存文档时发生错误 {}", msg);
                     break;
                 default:
-                    log.info("未知状态 user={}", userIds);
+                    log.warn("未知状态 {}", msg);
             }
         } catch (Exception ex) {
-            log.error("文档保存时发生错误 user={} msg={}", userIds, ex.getMessage());
+            log.error("文档保存时发生错误 {} msg={}", msg, ex.getMessage());
             return DocumentEditCallbackResponse.fail();
         }
         return DocumentEditCallbackResponse.success();

+ 13 - 8
src/main/java/com/github/jfcloud/excel/editor/docdeal/oss/service/OssTemplate.java

@@ -8,7 +8,6 @@ import com.amazonaws.auth.BasicAWSCredentials;
 import com.amazonaws.client.builder.AwsClientBuilder;
 import com.amazonaws.services.s3.AmazonS3;
 import com.amazonaws.services.s3.AmazonS3Client;
-import com.amazonaws.services.s3.AmazonS3ClientBuilder;
 import com.amazonaws.services.s3.model.*;
 import com.amazonaws.util.IOUtils;
 import com.github.jfcloud.excel.editor.docdeal.oss.OssProperties;
@@ -90,12 +89,12 @@ public class OssTemplate implements InitializingBean {
         }
     }
 
+    public boolean exist(String bucketName, String objectName) {
+        return amazonS3.doesObjectExist(bucketName, objectName);
+    }
+
     public S3Object getObject(String bucketName, String objectName) {
-        try {
-            return this.amazonS3.getObject(bucketName, objectName);
-        } catch (Throwable var4) {
-            throw var4;
-        }
+        return exist(bucketName, objectName) ? amazonS3.getObject(bucketName, objectName) : null;
     }
 
     /**
@@ -154,7 +153,7 @@ public class OssTemplate implements InitializingBean {
         return var4;
     }
 
-    public void removeObject(String bucketName, String objectName) throws Exception {
+    public void removeObject(String bucketName, String objectName) {
         this.amazonS3.deleteObject(bucketName, objectName);
     }
 
@@ -164,6 +163,12 @@ public class OssTemplate implements InitializingBean {
         AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(this.ossProperties.getEndpoint(), this.ossProperties.getRegion());
         AWSCredentials awsCredentials = new BasicAWSCredentials(this.ossProperties.getAccessKey(), this.ossProperties.getSecretKey());
         AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
-        this.amazonS3 = (AmazonS3) ((AmazonS3ClientBuilder) ((AmazonS3ClientBuilder) ((AmazonS3ClientBuilder) ((AmazonS3ClientBuilder) ((AmazonS3ClientBuilder) AmazonS3Client.builder().withEndpointConfiguration(endpointConfiguration)).withClientConfiguration(clientConfiguration)).withCredentials(awsCredentialsProvider)).disableChunkedEncoding()).withPathStyleAccessEnabled(this.ossProperties.getPathStyleAccess())).build();
+        this.amazonS3 = AmazonS3Client.builder()
+                .withEndpointConfiguration(endpointConfiguration)
+                .withClientConfiguration(clientConfiguration)
+                .withCredentials(awsCredentialsProvider)
+                .disableChunkedEncoding()
+                .withPathStyleAccessEnabled(ossProperties.getPathStyleAccess())
+                .build();
     }
 }

+ 52 - 0
src/main/resources/templates/notFound.html

@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
+<head>
+  <meta charset="UTF-8">
+  <meta content="width=device-width, initial-scale=1.0" name="viewport">
+  <title>文件不存在</title>
+  <style>
+      body {
+          font-family: 'Arial', sans-serif;
+          background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+          height: 100vh;
+          margin: 0;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          text-align: center;
+      }
+
+      .container {
+          background-color: white;
+          padding: 2rem 3rem;
+          border-radius: 15px;
+          box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
+          max-width: 500px;
+      }
+
+      h1 {
+          color: #e74c3c;
+          margin-bottom: 1.5rem;
+      }
+
+      p {
+          color: #555;
+          font-size: 1.1rem;
+          margin-bottom: 2rem;
+      }
+
+      .icon {
+          font-size: 5rem;
+          color: #e74c3c;
+          margin-bottom: 1rem;
+      }
+  </style>
+</head>
+<body>
+<div class="container">
+  <div class="icon">❌</div>
+  <h1>文件不存在</h1>
+  <p>您请求的文件 <em th:text="${name}"></em> 不存在,可能已被移动或删除。</p>
+</div>
+</body>
+</html>