浏览代码

优化文件管理

zhouhao 2 年之前
父节点
当前提交
90d5ea7872

+ 24 - 5
jetlinks-components/io-component/src/main/java/org/jetlinks/community/io/file/ClusterFileManager.java

@@ -4,7 +4,6 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
 import io.netty.buffer.ByteBufUtil;
 import io.netty.buffer.Unpooled;
-import io.scalecube.services.annotations.Service;
 import io.scalecube.services.annotations.ServiceMethod;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -55,7 +54,7 @@ public class ClusterFileManager implements FileManager {
     }
 
     @Override
-    public Mono<FileInfo> saveFile(FilePart filePart) {
+    public Mono<FileInfo> saveFile(FilePart filePart, FileOption... options) {
         return saveFile(filePart.filename(), filePart.content());
     }
 
@@ -66,7 +65,7 @@ public class ClusterFileManager implements FileManager {
         return dataBuffer;
     }
 
-    public Mono<FileInfo> doSaveFile(String name, Flux<DataBuffer> stream) {
+    public Mono<FileInfo> doSaveFile(String name, Flux<DataBuffer> stream, FileOption... options) {
         LocalDate now = LocalDate.now();
         FileInfo fileInfo = new FileInfo();
         fileInfo.setId(IDGenerator.MD5.generate());
@@ -93,10 +92,12 @@ public class ClusterFileManager implements FileManager {
                 if (!savedFile.exists()) {
                     return Mono.error(new BusinessException("error.file_storage_failed"));
                 }
+                fileInfo.withAccessKey(IDGenerator.MD5.generate());
                 fileInfo.setMd5(ByteBufUtil.hexDump(md5.digest()));
                 fileInfo.setSha256(ByteBufUtil.hexDump(sha256.digest()));
                 fileInfo.setLength(savedFile.length());
                 fileInfo.setCreateTime(System.currentTimeMillis());
+                fileInfo.setOptions(options);
                 FileEntity entity = FileEntity.of(fileInfo, storagePath, serverNodeId);
                 return repository
                     .insert(entity)
@@ -105,8 +106,26 @@ public class ClusterFileManager implements FileManager {
     }
 
     @Override
-    public Mono<FileInfo> saveFile(String name, Flux<DataBuffer> stream) {
-        return doSaveFile(name, stream);
+    public Mono<FileInfo> saveFile(String name, Flux<DataBuffer> stream, FileOption... options) {
+        return doSaveFile(name, stream, options);
+    }
+
+    @Override
+    public Mono<FileInfo> getFileByMd5(String md5) {
+        return repository
+            .createQuery()
+            .where(FileEntity::getMd5, md5)
+            .fetchOne()
+            .map(FileEntity::toInfo);
+    }
+
+    @Override
+    public Mono<FileInfo> getFileBySha256(String sha256) {
+        return repository
+            .createQuery()
+            .where(FileEntity::getSha256, sha256)
+            .fetchOne()
+            .map(FileEntity::toInfo);
     }
 
     @Override

+ 50 - 0
jetlinks-components/io-component/src/main/java/org/jetlinks/community/io/file/FileInfo.java

@@ -6,10 +6,16 @@ import org.apache.commons.io.FilenameUtils;
 import org.springframework.http.MediaType;
 import org.springframework.util.StringUtils;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
 @Getter
 @Setter
 public class FileInfo {
 
+    public static final String OTHER_ACCESS_KEY = "accessKey";
+
     private String id;
 
     private String name;
@@ -28,6 +34,8 @@ public class FileInfo {
 
     private FileOption[] options;
 
+    private Map<String, Object> others;
+
     public MediaType mediaType() {
         if (!StringUtils.hasText(extension)) {
             return MediaType.APPLICATION_OCTET_STREAM;
@@ -36,6 +44,14 @@ public class FileInfo {
             case "jpg":
             case "jpeg":
                 return MediaType.IMAGE_JPEG;
+            case "png":
+                return MediaType.IMAGE_PNG;
+            case "gif":
+                return MediaType.IMAGE_GIF;
+            case "mp4":
+                return MediaType.parseMediaType("video/mp4");
+            case "flv":
+                return MediaType.parseMediaType("video/x-flv");
             case "text":
             case "txt":
                 return MediaType.TEXT_PLAIN;
@@ -46,10 +62,44 @@ public class FileInfo {
         }
     }
 
+
+    public boolean hasOption(FileOption option){
+        if(options==null){
+            return false;
+        }
+        for (FileOption fileOption : options) {
+            if(fileOption==option){
+                return true;
+            }
+        }
+        return false;
+    }
     public FileInfo withFileName(String fileName) {
         name = fileName;
         extension = FilenameUtils.getExtension(fileName);
         return this;
     }
 
+    public synchronized FileInfo withOther(String key, Object value) {
+        if (others == null) {
+            others = new HashMap<>();
+        }
+        others.put(key, value);
+        return this;
+    }
+
+    public FileInfo withAccessKey(String accessKey) {
+        withOther(OTHER_ACCESS_KEY, accessKey);
+        return this;
+    }
+
+    public Optional<String> accessKey() {
+        return Optional
+            .ofNullable(others)
+            .map(map -> map.get(OTHER_ACCESS_KEY))
+            .map(String::valueOf)
+            .filter(StringUtils::hasText);
+    }
+
+
 }

+ 11 - 5
jetlinks-components/io-component/src/main/java/org/jetlinks/community/io/file/FileManager.java

@@ -7,23 +7,29 @@ import reactor.core.publisher.Mono;
 
 import java.util.function.Function;
 
-
+/**
+ * 文件管理器,统一管理文件信息
+ */
 public interface FileManager {
 
-    Mono<FileInfo> saveFile(FilePart filePart);
+    Mono<FileInfo> saveFile(FilePart filePart, FileOption... options);
 
-    Mono<FileInfo> saveFile(String name, Flux<DataBuffer> stream);
+    Mono<FileInfo> saveFile(String name, Flux<DataBuffer> stream, FileOption... options);
 
     Mono<FileInfo> getFile(String id);
 
+    Mono<FileInfo> getFileByMd5(String md5);
+
+    Mono<FileInfo> getFileBySha256(String sha256);
+
     Flux<DataBuffer> read(String id);
 
     Flux<DataBuffer> read(String id, long position);
 
     Flux<DataBuffer> read(String id,
-                          Function<ReaderContext,Mono<Void>> beforeRead);
+                          Function<ReaderContext, Mono<Void>> beforeRead);
 
-    interface ReaderContext{
+    interface ReaderContext {
         FileInfo info();
 
         void position(long position);

+ 1 - 1
jetlinks-components/io-component/src/main/java/org/jetlinks/community/io/file/FileManagerConfiguration.java

@@ -9,7 +9,7 @@ import org.springframework.context.annotation.Configuration;
 
 @Configuration
 @EnableConfigurationProperties(FileProperties.class)
-@EnableEasyormRepository("org.jetlinks.community.io.file.FileEntity")
+@EnableEasyormRepository("org.jetlinks.pro.io.file.FileEntity")
 public class FileManagerConfiguration {
 
 

+ 56 - 22
jetlinks-components/io-component/src/main/java/org/jetlinks/community/io/file/web/FileManagerController.java

@@ -2,13 +2,17 @@ package org.jetlinks.community.io.file.web;
 
 import io.swagger.v3.oas.annotations.Operation;
 import lombok.AllArgsConstructor;
+import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.exception.AccessDenyException;
 import org.jetlinks.community.io.file.FileInfo;
 import org.jetlinks.community.io.file.FileManager;
+import org.jetlinks.community.io.file.FileOption;
 import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpRange;
 import org.springframework.http.MediaType;
 import org.springframework.http.codec.multipart.FilePart;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
@@ -31,7 +35,7 @@ public class FileManagerController {
     }
 
     @GetMapping("/{fileId}")
-    @Authorize(merge = false)
+    @Authorize(ignore = true)
     @Operation(summary = "获取文件")
     public Mono<Void> read(@PathVariable String fileId,
                            ServerWebExchange exchange) {
@@ -40,28 +44,58 @@ public class FileManagerController {
             .getResponse()
             .writeWith(fileManager
                            .read(fileId, ctx -> {
-                               List<HttpRange> ranges = exchange
-                                   .getRequest()
-                                   .getHeaders()
-                                   .getRange();
-                               long position = 0;
-                               if (ranges.size() != 0) {
-                                   position = ranges.get(0).getRangeStart(ctx.info().getLength());
-                               }
-                               ctx.position(position);
-                               MediaType mediaType = ctx.info().mediaType();
-                               exchange.getResponse().getHeaders().setContentType(mediaType);
-                               exchange.getResponse().getHeaders().setContentLength(ctx.info().getLength());
-                               //文件流时下载文件
-                               if (mediaType.includes(MediaType.APPLICATION_OCTET_STREAM)) {
-                                   exchange.getResponse().getHeaders().setContentDisposition(
-                                       ContentDisposition
-                                           .builder("attachment")
-                                           .filename(ctx.info().getName(), StandardCharsets.UTF_8)
-                                           .build()
-                                   );
+                               Mono<Void> before;
+                               //不是公开访问则需要登陆或者使用accessKey
+                               if (!ctx.info().hasOption(FileOption.publicAccess)) {
+                                   String key = exchange.getRequest().getQueryParams().getFirst("accessKey");
+                                   //请求参数没有accessKey则校验当前用户是否登陆
+                                   if (!StringUtils.hasText(key)) {
+                                       before = Authentication
+                                           .currentReactive()
+                                           .switchIfEmpty(Mono.error(AccessDenyException::new))
+                                           .then();
+                                   } else {
+                                       //校验accessKey
+                                       if (ctx.info().accessKey().map(key::equalsIgnoreCase).orElse(false)) {
+                                           before = Mono.empty();
+                                       } else {
+                                           before = Mono.error(AccessDenyException::new);
+                                       }
+                                   }
+                               } else {
+                                   before = Mono.empty();
                                }
-                               return Mono.empty();
+
+                               return before.then(
+                                   Mono.fromRunnable(() -> {
+                                       List<HttpRange> ranges = exchange
+                                           .getRequest()
+                                           .getHeaders()
+                                           .getRange();
+                                       long position = 0;
+                                       if (ranges.size() != 0) {
+                                           position = ranges.get(0).getRangeStart(ctx.info().getLength());
+                                       }
+                                       ctx.position(position);
+                                       MediaType mediaType = ctx.info().mediaType();
+                                       exchange.getResponse().getHeaders().setContentType(mediaType);
+                                       exchange.getResponse().getHeaders().setContentLength(ctx.info().getLength());
+                                       exchange.getResponse().getHeaders().add("file-md5", ctx.info().getMd5());
+                                       exchange.getResponse().getHeaders().add("file-sha256", ctx.info().getSha256());
+
+                                       //文件流时下载文件
+                                       if (mediaType.includes(MediaType.APPLICATION_OCTET_STREAM)) {
+                                           exchange.getResponse().getHeaders().setContentDisposition(
+                                               ContentDisposition
+                                                   .builder("attachment")
+                                                   .filename(ctx.info().getName(), StandardCharsets.UTF_8)
+                                                   .build()
+                                           );
+                                       }
+                                   })
+                               );
+
+
                            }));
     }
 }