Просмотр исходного кода

删除和 s3 相关的所有代码, 不再作为一个 s3 的实现

reghao 2 лет назад
Родитель
Сommit
c529923116
22 измененных файлов с 449 добавлено и 779 удалено
  1. 0 40
      dfs-store/src/main/java/cn/reghao/dfs/store/cache/CacheTask.java
  2. 0 49
      dfs-store/src/main/java/cn/reghao/dfs/store/cache/LocalCache.java
  3. 0 54
      dfs-store/src/main/java/cn/reghao/dfs/store/controller/MultipartUploadController.java
  4. 27 119
      dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectMultipartUploadController.java
  5. 11 62
      dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectUploadController.java
  6. 0 15
      dfs-store/src/main/java/cn/reghao/dfs/store/model/dto/PostObject.java
  7. 0 20
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/CompleteMultipartUpload.java
  8. 0 28
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/CompleteMultipartUploadResult.java
  9. 0 41
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Contents.java
  10. 0 33
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/CopyPartResult.java
  11. 0 23
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/InitiateMultipartUploadResult.java
  12. 0 16
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Initiator.java
  13. 0 35
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/ListPartsResult.java
  14. 0 16
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Owner.java
  15. 0 28
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Part.java
  16. 0 20
      dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/PostResponse.java
  17. 3 21
      dfs-store/src/main/java/cn/reghao/dfs/store/service/GetObjectService.java
  18. 0 152
      dfs-store/src/main/java/cn/reghao/dfs/store/service/MultipartUploadService.java
  19. 133 6
      dfs-store/src/main/java/cn/reghao/dfs/store/service/ObjectMultipartUploadService.java
  20. 97 0
      dfs-store/src/main/java/cn/reghao/dfs/store/task/ImageFileService.java
  21. 178 0
      dfs-store/src/main/java/cn/reghao/dfs/store/util/media/ImageOps.java
  22. 0 1
      dfs-store/src/test/java/FileMetaTest.java

+ 0 - 40
dfs-store/src/main/java/cn/reghao/dfs/store/cache/CacheTask.java

@@ -1,40 +0,0 @@
-package cn.reghao.dfs.store.cache;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Map;
-
-/**
- * @author reghao
- * @date 2023-04-25 18:39:04
- */
-public class CacheTask implements Runnable {
-    private Map<String, String> cacheMap;
-    private final String cacheDir;
-    private final String objectId;
-    private final String absolutePath;
-
-    public CacheTask(Map<String, String> cacheMap, String cacheDir, String objectId, String absolutePath) {
-        this.cacheMap = cacheMap;
-        this.cacheDir = cacheDir;
-        this.objectId = objectId;
-        this.absolutePath = absolutePath;
-    }
-
-    @Override
-    public void run() {
-        String cachePath = String.format("%s/%s", cacheDir, objectId);
-        try {
-            Files.copy(Path.of(absolutePath), Path.of(cachePath));
-            cacheMap.put(objectId, cachePath);
-        } catch (IOException e) {
-            e.printStackTrace();
-            try {
-                Files.delete(Path.of(cachePath));
-            } catch (IOException ex) {
-                ex.printStackTrace();
-            }
-        }
-    }
-}

+ 0 - 49
dfs-store/src/main/java/cn/reghao/dfs/store/cache/LocalCache.java

@@ -1,49 +0,0 @@
-package cn.reghao.dfs.store.cache;
-
-import cn.reghao.dfs.store.config.OssProperties;
-import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-
-/**
- * @author reghao
- * @date 2023-04-25 18:24:17
- */
-@Slf4j
-@Service
-public class LocalCache {
-    private final String cacheDir;
-    private final Map<String, String> cacheMap;
-    private final ExecutorService threadPool = ThreadPoolWrapper.threadPool("cache-poll", 10);
-
-    public LocalCache(OssProperties ossProperties) {
-        this.cacheDir = ossProperties.getCacheDir();
-        this.cacheMap = new HashMap<>();
-        init(cacheDir);
-    }
-
-    private void init(String cacheDir) {
-        File dir = new File(cacheDir);
-        Arrays.stream(dir.listFiles()).forEach(file -> {
-            String objectId = file.getName();
-            String absolutePath = file.getAbsolutePath();
-            cacheMap.put(objectId, absolutePath);
-        });
-    }
-
-    public String getCache(String objectId) {
-        return cacheMap.get(objectId);
-    }
-
-    public void putCache(String objectId, String absolutePath) {
-        CacheTask task = new CacheTask(cacheMap, cacheDir, objectId, absolutePath);
-        log.info("添加缓存任务 {}", absolutePath);
-        threadPool.submit(task);
-    }
-}

+ 0 - 54
dfs-store/src/main/java/cn/reghao/dfs/store/controller/MultipartUploadController.java

@@ -1,54 +0,0 @@
-package cn.reghao.dfs.store.controller;
-
-import cn.reghao.dfs.store.model.dto.*;
-import cn.reghao.dfs.store.service.MultipartUploadService;
-import cn.reghao.jutil.jdk.result.WebResult;
-import org.springframework.http.MediaType;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author reghao
- * @date 2023-05-11 17:45:02
- */
-@RestController
-public class MultipartUploadController {
-    private final MultipartUploadService multipartUploadService;
-
-    public MultipartUploadController(MultipartUploadService multipartUploadService) {
-        this.multipartUploadService = multipartUploadService;
-    }
-
-    @PostMapping(value = "/", params = {"create"}, produces = MediaType.APPLICATION_JSON_VALUE)
-    public String videoFilePrepare(@Validated UploadPrepare uploadPrepare) {
-        UploadPrepareRet uploadPrepareRet = multipartUploadService.prepareUpload(uploadPrepare);
-        return WebResult.success(uploadPrepareRet);
-    }
-
-    @GetMapping(value = "/", params = {"multipart"}, produces = MediaType.APPLICATION_JSON_VALUE)
-    public String uploadedVideoFilePart() {
-        Map<String, Object> map = new HashMap<>();
-        map.put("skipUpload", false);
-        map.put("url", "");
-        map.put("needMerge", false);
-        map.put("uploaded", Collections.emptyList());
-        return WebResult.success(map);
-    }
-
-    @PostMapping(value = "/", params = {"multipart"}, produces = MediaType.APPLICATION_JSON_VALUE)
-    public String uploadVideoFile0(@Validated UploadFilePart uploadFilePart) throws Exception {
-        FilePartRet uploadedFileRet = multipartUploadService.putFilePart(uploadFilePart);
-        return WebResult.success(uploadedFileRet);
-    }
-
-    @PostMapping(value = "/", params = {"merge"}, produces = MediaType.APPLICATION_JSON_VALUE)
-    public String merge() {
-        return WebResult.success();
-    }
-}

+ 27 - 119
dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectMultipartUploadController.java

@@ -1,22 +1,15 @@
 package cn.reghao.dfs.store.controller;
 
-import cn.reghao.dfs.store.model.s3.*;
-import cn.reghao.dfs.store.service.FileStoreService;
-import cn.reghao.dfs.store.util.s3.OssUtil;
-import cn.reghao.jutil.jdk.converter.ByteHex;
+import cn.reghao.dfs.store.model.dto.FilePartRet;
+import cn.reghao.dfs.store.model.dto.UploadFilePart;
+import cn.reghao.dfs.store.model.dto.UploadPrepare;
+import cn.reghao.dfs.store.model.dto.UploadPrepareRet;
+import cn.reghao.dfs.store.service.ObjectMultipartUploadService;
 import cn.reghao.jutil.jdk.result.WebResult;
-import cn.reghao.jutil.jdk.security.Base64Util;
-import cn.reghao.jutil.jdk.security.DigestUtil;
 import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.*;
 
 /**
@@ -25,121 +18,36 @@ import java.util.*;
  */
 @RestController
 public class ObjectMultipartUploadController {
-    private static final Map<String, Map<Long, String>> map = new TreeMap<>();
-    private final FileStoreService storeService;
+    private final ObjectMultipartUploadService objectMultipartUploadService;
 
-    public ObjectMultipartUploadController(FileStoreService storeService) {
-        this.storeService = storeService;
+    public ObjectMultipartUploadController(ObjectMultipartUploadService objectMultipartUploadService) {
+        this.objectMultipartUploadService = objectMultipartUploadService;
     }
 
-    @PostMapping(value = "/**", params = {"uploads"})
-    public ResponseEntity<InitiateMultipartUploadResult> initiateMultipartUpload() {
-        String objectName = OssUtil.getObjectName();
-
-        String bucket = "tnb";
-        String uploadId = UUID.randomUUID().toString().replace("-", "");
-        map.put(uploadId, new HashMap<>());
-        InitiateMultipartUploadResult result = new InitiateMultipartUploadResult(bucket, objectName, uploadId);
-        return ResponseEntity.ok()
-                .contentType(MediaType.APPLICATION_XML)
-                .body(result);
+    @PostMapping(value = "/", params = {"create"}, produces = MediaType.APPLICATION_JSON_VALUE)
+    public String videoFilePrepare(@Validated UploadPrepare uploadPrepare) {
+        UploadPrepareRet uploadPrepareRet = objectMultipartUploadService.prepareUpload(uploadPrepare);
+        return WebResult.success(uploadPrepareRet);
     }
 
-    @PutMapping(value = "/**", params = {"partNumber", "uploadId"})
-    public ResponseEntity<String> uploadPart(@RequestParam("partNumber") long partNumber,
-                                             @RequestParam("uploadId") String uploadId,
-                                             @RequestBody File file) throws IOException, NoSuchAlgorithmException {
-
-        map.get(uploadId).put(partNumber, file.getAbsolutePath());
-
-        FileInputStream fis = new FileInputStream(file);
-        byte[] md5 = DigestUtil.md5sum(fis.readAllBytes());
-        String md5Hex = ByteHex.bytes2Hex(md5);
-        String md5Base64 = Base64Util.encode(md5);
-
-        DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", Locale.CHINA);
-        df.setTimeZone(TimeZone.getTimeZone("GMT"));
-        String dateStr = df.format(new Date());
-
-        return ResponseEntity.ok()
-                .header("x-amz-request-id", UUID.randomUUID().toString().replace("-", ""))
-                .header("Date", dateStr)
-                .header("Content-Length", String.valueOf(file.length()))
-                .header("Connection", "keep-alive")
-                .header("Server", "TNBCloudOSS")
-                .eTag(md5Hex)
-                .build();
+    @GetMapping(value = "/", params = {"multipart"}, produces = MediaType.APPLICATION_JSON_VALUE)
+    public String uploadedVideoFilePart() {
+        Map<String, Object> map = new HashMap<>();
+        map.put("skipUpload", false);
+        map.put("url", "");
+        map.put("needMerge", false);
+        map.put("uploaded", Collections.emptyList());
+        return WebResult.success(map);
     }
 
-    @PostMapping(value = "/**", params = {"uploadId"}, consumes = MediaType.APPLICATION_XML_VALUE)
-    public ResponseEntity<CompleteMultipartUploadResult> completeMultipartUpload(@RequestParam("uploadId") String uploadId,
-                                                                                 @RequestBody CompleteMultipartUpload completeMultipartUpload) {
-        List<Part> parts = completeMultipartUpload.getParts();
-        String absolutePath = "/home/reghao/Downloads/test.dat";
-        try {
-            int start = 0;
-            for (Map.Entry<Long, String> entry : map.get(uploadId).entrySet()) {
-                Long index = entry.getKey();
-                String path = entry.getValue();
-                File file = new File(path);
-                long length = file.length();
-                FileInputStream fis = new FileInputStream(file);
-                storeService.writeToFile(fis, absolutePath, start);
-                start += length;
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        String location = "oss.reghao.cn";
-        String bucket = "tnb";
-        String key = "a/b/c";
-        String eTag = "1234567890abcde";
-        CompleteMultipartUploadResult result = new CompleteMultipartUploadResult(location, bucket, key, eTag);
-        return ResponseEntity.ok()
-                .contentType(MediaType.APPLICATION_XML)
-                .body(result);
+    @PostMapping(value = "/", params = {"multipart"}, produces = MediaType.APPLICATION_JSON_VALUE)
+    public String uploadVideoFile0(@Validated UploadFilePart uploadFilePart) throws Exception {
+        FilePartRet uploadedFileRet = objectMultipartUploadService.putFilePart(uploadFilePart);
+        return WebResult.success(uploadedFileRet);
     }
 
-    @DeleteMapping(value = "/**", params = {"uploadId"})
-    public String abortMultipartUpload(@RequestParam("uploadId") String uploadId) {
-        Map<Long, String> fileMap = map.get(uploadId);
-
+    @PostMapping(value = "/", params = {"merge"}, produces = MediaType.APPLICATION_JSON_VALUE)
+    public String merge() {
         return WebResult.success();
     }
-
-    @GetMapping(value = "/**", params = {"uploadId"})
-    public ResponseEntity<ListPartsResult> listParts(@RequestParam("uploadId") String uploadId,
-                                                     @RequestParam(value = "encodingType", required = false) String encodingType,
-                                                     @RequestParam(value = "maxParts", required = false) String maxParts,
-                                                     @RequestParam(value = "partNumberMarker", required = false) String partNumberMarker) {
-        ListPartsResult listPartsResult = new ListPartsResult();
-        listPartsResult.setBucket("");
-        listPartsResult.setKey("");
-        listPartsResult.setUploadId("");
-        listPartsResult.setInitiator(new Initiator());
-        listPartsResult.setOwner(new Owner());
-        listPartsResult.setStorageClass("");
-        listPartsResult.setPartNumberMarker(10);
-        listPartsResult.setNextPartNumberMarker(10);
-        // 表示列表已完全返回
-        listPartsResult.setTruncated(false);
-
-        List<Part> list = new ArrayList<>();
-        list.add(new Part());
-        listPartsResult.setParts(list);
-
-        return ResponseEntity.ok()
-                .contentType(MediaType.APPLICATION_XML)
-                .body(listPartsResult);
-    }
-
-    @PutMapping(value = "/**", params = {"partNumber", "uploadId"}, headers = {"x-amz-copy-source"})
-    public ResponseEntity<CopyPartResult> uploadPartCopy(@RequestParam("partNumber") long partNumber,
-                                                         @RequestParam("uploadId") String uploadId) {
-        CopyPartResult result = new CopyPartResult();
-        return ResponseEntity.ok()
-                .contentType(MediaType.APPLICATION_XML)
-                .body(result);
-    }
 }

+ 11 - 62
dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectUploadController.java

@@ -1,16 +1,12 @@
 package cn.reghao.dfs.store.controller;
 
 import cn.reghao.dfs.store.model.dto.PostObject;
-import cn.reghao.dfs.store.model.s3.PostResponse;
-import cn.reghao.dfs.store.redis.ds.RedisString;
 import cn.reghao.dfs.store.service.PutObjectService;
-import cn.reghao.dfs.store.util.s3.OssUtil;
 import cn.reghao.jutil.jdk.converter.ByteHex;
 import cn.reghao.jutil.jdk.result.WebResult;
 import cn.reghao.jutil.jdk.security.Base64Util;
 import cn.reghao.jutil.jdk.security.DigestUtil;
 import cn.reghao.jutil.web.ServletUtil;
-import org.springframework.http.ResponseEntity;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
@@ -21,11 +17,8 @@ import org.springframework.web.multipart.MultipartFile;
 import javax.servlet.http.HttpServletRequest;
 import java.io.File;
 import java.io.FileInputStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import java.util.UUID;
 
 /**
@@ -35,83 +28,40 @@ import java.util.UUID;
 @RestController
 public class ObjectUploadController {
     private final PutObjectService putObjectService;
-    private final RedisString redisString;
 
-    public ObjectUploadController(PutObjectService putObjectService, RedisString redisString) {
+    public ObjectUploadController(PutObjectService putObjectService) {
         this.putObjectService = putObjectService;
-        this.redisString = redisString;
     }
 
     @PutMapping(value = "/**")
-    public ResponseEntity<String> putObject(@RequestBody File file) throws Exception {
+    public String putObject(@RequestBody File file) throws Exception {
         HttpServletRequest servletRequest = ServletUtil.getRequest();
-        String host = servletRequest.getHeader("host");
-        String contentType = servletRequest.getHeader("content-type");
         String contentMd5 = servletRequest.getHeader("content-md5");
         String sha256sum = servletRequest.getHeader("x-amz-content-sha256");
-        String date = servletRequest.getHeader("x-amz-date");
 
         FileInputStream fis = new FileInputStream(file);
         byte[] md5 = DigestUtil.md5sum(fis.readAllBytes());
         String md5Hex = ByteHex.bytes2Hex(md5);
         String md5Base64 = Base64Util.encode(md5);
         if (!contentMd5.equals(md5Base64)) {
-            return ResponseEntity.status(500).body("md5 不匹配");
+            return WebResult.failWithMsg("md5 不匹配");
         }
 
         fis = new FileInputStream(file);
         String sha256sum1 = DigestUtil.sha256sum(fis);
         if (!sha256sum.equals(sha256sum1)) {
-            return ResponseEntity.status(500).body("sha256sum 不匹配");
+            return WebResult.failWithMsg("sha256sum 不匹配");
         }
 
-        String accessKey = OssUtil.getAccessKey();
-        /*if (accessKey == null) {
-            return ResponseEntity.status(500).body("accessKeyId 不存在");
-        } else {
-            String secretKey = redisString.get(accessKey);
-            if (secretKey == null) {
-                return ResponseEntity.status(500).body("secretKey 不存在");
-            }
-
-            boolean matched = OssUtil.matchSignature(secretKey, sha256sum1);
-            if (!matched) {
-                return ResponseEntity.status(500).body("签名不正确");
-            }
-        }*/
-
-        String objectName = OssUtil.getObjectName();
+        String uri = servletRequest.getRequestURI();
+        String decodedUri = URLDecoder.decode(uri, StandardCharsets.UTF_8);
+        String objectName = decodedUri.replaceFirst("/", "");
         putObjectService.putObject(objectName, file, sha256sum);
-
-        DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", Locale.CHINA);
-        df.setTimeZone(TimeZone.getTimeZone("GMT"));
-        String dateStr = df.format(new Date());
-        return ResponseEntity.ok()
-                .header("x-amz-request-id", UUID.randomUUID().toString().replace("-", ""))
-                .header("Date", dateStr)
-                .header("Content-Length", "1")
-                .header("Connection", "close")
-                .header("Server", "TNBCloudOSS")
-                .eTag(md5Hex)
-                .build();
+        return WebResult.success();
     }
 
     @PostMapping(value = "/")
     public String postObject(@Validated PostObject postObject) throws Exception {
-        String credential = postObject.getXAmzCredential();
-        String signature = postObject.getXAmzSignature();
-        String policy = postObject.getPolicy();
-
-        /*String str[] = credential.split("/");
-        String accessKeyId = str[0];
-        String dateStr = str[1];
-        String regionName = str[2];
-        String serviceName = str[3];
-
-        String secretAccessKey = redisString.get(accessKeyId);
-        byte[] signingKey = OssUtil.getSigningKey(secretAccessKey, dateStr, regionName, serviceName);*/
-
-        String objectName = postObject.getKey();
         MultipartFile multipartFile = postObject.getFile();
         String tmpPath = String.format("/opt/tmp/tomcat/%s", UUID.randomUUID().toString().replace("-", ""));
         File tmpFile = new File(tmpPath);
@@ -121,7 +71,6 @@ public class ObjectUploadController {
         String sha256sum = DigestUtil.sha256sum(fis);
         putObjectService.putObject(objectName, tmpFile, sha256sum);
         tmpFile.delete();*/
-        PostResponse postResponse = new PostResponse();
-        return WebResult.success(postResponse);
+        return WebResult.success();
     }
 }

+ 0 - 15
dfs-store/src/main/java/cn/reghao/dfs/store/model/dto/PostObject.java

@@ -19,21 +19,6 @@ import java.io.Serializable;
 public class PostObject implements Serializable {
     private static final long serialVersionUID = 1L;
 
-    private String cacheControl;
-    private String contentType;
-    private String contentDisposition;
-    private String contentEncoding;
-    private String expires;
-    private String policy;
-    private String successActionRedirect;
-    private String successActionStatus;
-    private String xAmzAlgorithm;
-    private String xAmzCredential;
-    private String xAmzDate;
-    private String xAmzSignature;
-    private Boolean xAmzStorageClass;
-    //@NotBlank
-    private String key;
     @NotNull
     private Boolean xAmzUpload;
     @NotNull

+ 0 - 20
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/CompleteMultipartUpload.java

@@ -1,20 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-
-import javax.xml.bind.annotation.*;
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2022-12-09 16:46:04
- */
-@NoArgsConstructor
-@Getter
-@XmlRootElement(name = "CompleteMultipartUpload")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class CompleteMultipartUpload {
-    @XmlElements(value = {@XmlElement(name = "Part", type = Part.class)})
-    private List<Part> parts;
-}

+ 0 - 28
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/CompleteMultipartUploadResult.java

@@ -1,28 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import lombok.AllArgsConstructor;
-import lombok.NoArgsConstructor;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-
-/**
- * @author reghao
- * @date 2023-03-23 11:36:21
- */
-@NoArgsConstructor
-@AllArgsConstructor
-@XmlRootElement(name = "CompleteMultipartUploadResult")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class CompleteMultipartUploadResult {
-    @XmlElement(name = "Location")
-    private String location;
-    @XmlElement(name = "Bucket")
-    private String bucket;
-    @XmlElement(name = "Key")
-    private String key;
-    @XmlElement(name = "ETag")
-    private String eTag;
-}

+ 0 - 41
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Contents.java

@@ -1,41 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import java.time.LocalDateTime;
-
-/**
- * @author reghao
- * @date 2022-12-08 11:09:12
- */
-@NoArgsConstructor
-@Getter
-@Setter
-@XmlRootElement(name = "Contents")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Contents {
-    @XmlElement(name = "Key")
-    private String key;
-    @XmlElement(name = "LastModified")
-    private LocalDateTime lastModified;
-    @XmlElement(name = "ETag")
-    private String eTag;
-    @XmlElement(name = "Size")
-    private long size;
-    @XmlElement(name = "StorageClass")
-    private String storageClass;
-
-    public Contents(String key, LocalDateTime lastModified, String sha256sum, long size) {
-        this.key = key;
-        this.lastModified = lastModified;
-        this.eTag = sha256sum;
-        this.size = size;
-        this.storageClass = "STANDARD";
-    }
-}

+ 0 - 33
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/CopyPartResult.java

@@ -1,33 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import java.util.Date;
-
-/**
- * @author reghao
- * @date 2023-03-24 14:21:34
- */
-@NoArgsConstructor
-@AllArgsConstructor
-@Setter
-@Getter
-@XmlRootElement(name = "CopyPartResult")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class CopyPartResult {
-    @XmlElement(name = "ETag")
-    private String eTag;
-    @XmlElement(name = "LastModified")
-    private Date lastModifiedDate;
-    /*@XmlElement(name = "VersionId")
-    private String versionId;
-    @XmlElement(name = "PartNumber")
-    private int partNumber;*/
-}

+ 0 - 23
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/InitiateMultipartUploadResult.java

@@ -1,23 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import lombok.AllArgsConstructor;
-import lombok.NoArgsConstructor;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-
-/**
- * @author reghao
- * @date 2022-11-21 15:45:15
- */
-@NoArgsConstructor
-@AllArgsConstructor
-@XmlRootElement(name = "InitiateMultipartUploadResult")
-public class InitiateMultipartUploadResult {
-    @XmlElement(name = "Bucket")
-    private String bucket;
-    @XmlElement(name = "Key")
-    private String key;
-    @XmlElement(name = "UploadId")
-    private String uploadId;
-}

+ 0 - 16
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Initiator.java

@@ -1,16 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-
-/**
- * @author reghao
- * @date 2022-12-09 16:38:50
- */
-@XmlRootElement(name = "Initiator")
-public class Initiator {
-    @XmlElement(name = "ID")
-    private String id;
-    @XmlElement(name = "DisplayName")
-    private String displayName;
-}

+ 0 - 35
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/ListPartsResult.java

@@ -1,35 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import lombok.Setter;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2022-12-09 16:35:01
- */
-@Setter
-@XmlRootElement(name = "ListPartsResult")
-public class ListPartsResult {
-    @XmlElement(name = "Bucket")
-    private String bucket;
-    @XmlElement(name = "Key")
-    private String key;
-    @XmlElement(name = "UploadId")
-    private String uploadId;
-    @XmlElement(name = "Initiator")
-    private Initiator initiator;
-    @XmlElement(name = "Owner")
-    private Owner owner;
-    @XmlElement(name = "StorageClass")
-    private String storageClass;
-    @XmlElement(name = "PartNumberMarker")
-    private int partNumberMarker;
-    @XmlElement(name = "NextPartNumberMarker")
-    private int nextPartNumberMarker;
-    @XmlElement(name = "IsTruncated")
-    private boolean isTruncated;
-    private List<Part> parts;
-}

+ 0 - 16
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Owner.java

@@ -1,16 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-
-/**
- * @author reghao
- * @date 2022-11-21 17:31:06
- */
-@XmlRootElement(name = "Owner")
-public class Owner {
-    @XmlElement(name = "ID")
-    private String id;
-    @XmlElement(name = "DisplayName")
-    private String displayName;
-}

+ 0 - 28
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/Part.java

@@ -1,28 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-import javax.xml.bind.annotation.*;
-import java.time.LocalDateTime;
-
-/**
- * @author reghao
- * @date 2022-12-09 16:37:57
- */
-@NoArgsConstructor
-@Setter
-@Getter
-@XmlRootElement(name = "Part")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Part {
-    @XmlElement(name = "PartNumber")
-    private int partNumber;
-    @XmlElement(name = "LastModified")
-    private LocalDateTime lastModified;
-    @XmlElement(name = "ETag")
-    private String eTag;
-    @XmlElement(name = "Size")
-    private long size;
-}

+ 0 - 20
dfs-store/src/main/java/cn/reghao/dfs/store/model/s3/PostResponse.java

@@ -1,20 +0,0 @@
-package cn.reghao.dfs.store.model.s3;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-
-/**
- * @author reghao
- * @date 2022-12-28 16:20:52
- */
-@XmlRootElement(name = "PostResponse")
-public class PostResponse {
-    @XmlElement(name = "Bucket")
-    private String bucket;
-    @XmlElement(name = "Key")
-    private String key;
-    @XmlElement(name = "ETag")
-    private String eTag;
-    @XmlElement(name = "Location")
-    private String location;
-}

+ 3 - 21
dfs-store/src/main/java/cn/reghao/dfs/store/service/GetObjectService.java

@@ -1,6 +1,5 @@
 package cn.reghao.dfs.store.service;
 
-import cn.reghao.dfs.store.cache.LocalCache;
 import cn.reghao.dfs.store.db.mapper.FileMetaMapper;
 import cn.reghao.dfs.store.db.repository.FileMetaRepository;
 import cn.reghao.dfs.store.model.dto.ContentRange;
@@ -28,12 +27,10 @@ public class GetObjectService {
     private final FileMetaMapper fileMetaMapper;
     // 10MiB
     private final int bufSize = 1024*1024*10;
-    private final LocalCache localCache;
     private final FileMetaRepository fileMetaRepository;
 
-    public GetObjectService(FileMetaMapper fileMetaMapper, LocalCache localCache, FileMetaRepository fileMetaRepository) {
+    public GetObjectService(FileMetaMapper fileMetaMapper, FileMetaRepository fileMetaRepository) {
         this.fileMetaMapper = fileMetaMapper;
-        this.localCache = localCache;
         this.fileMetaRepository = fileMetaRepository;
     }
     
@@ -132,24 +129,9 @@ public class GetObjectService {
         response.setHeader("Accept-Ranges", "bytes");
         response.setHeader("Content-Range", "bytes "+start+"-"+(end-1)+"/"+contentLength);
 
-        String cachePath = localCache.getCache(objectMeta.getObjectId());
-        if (cachePath == null || !Files.exists(Path.of(cachePath))) {
-            cachePath = objectMeta.getAbsolutePath();
-        }
-
+        String absolutePath = objectMeta.getAbsolutePath();
         OutputStream outputStream = response.getOutputStream();
-        writeResponse(outputStream, cachePath, start, end);
-
-        /*RandomAccessFile raf = new RandomAccessFile(cachePath, "r");
-        raf.seek(start);
-        byte[] buf = new byte[bufSize];
-        int readLen;
-        while ((readLen = raf.read(buf, 0, bufSize)) != -1) {
-            outputStream.write(buf, 0, readLen);
-            outputStream.flush();
-        }
-        outputStream.close();
-        raf.close();*/
+        writeResponse(outputStream, absolutePath, start, end);
     }
 
     private void writeDownloadContent(ObjectMeta objectMeta) throws IOException {

+ 0 - 152
dfs-store/src/main/java/cn/reghao/dfs/store/service/MultipartUploadService.java

@@ -1,152 +0,0 @@
-package cn.reghao.dfs.store.service;
-
-import cn.reghao.dfs.store.db.mapper.DataBlockMapper;
-import cn.reghao.dfs.store.db.mapper.FileMetaMapper;
-import cn.reghao.dfs.store.model.dto.*;
-import cn.reghao.dfs.store.model.po.*;
-import cn.reghao.jutil.jdk.security.DigestUtil;
-import cn.reghao.jutil.jdk.shell.Shell;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.*;
-import java.util.*;
-
-/**
- * @author reghao
- * @date 2023-05-19 10:09:01
- */
-@Slf4j
-@Service
-public class MultipartUploadService {
-    // 20MiB
-    private final static long PART_SIZE = 1024*1024*20;
-    private final FileMetaMapper fileMetaMapper;
-    private final DataBlockMapper dataBlockMapper;
-    private final FileUrlService fileUrlService;
-    private final FileStoreService fileStoreService;
-    private final Map<String, Set<Integer>> map = new HashMap<>();
-    private final Map<String, PathUrl> pathMap = new HashMap<>();
-
-    public MultipartUploadService(FileMetaMapper fileMetaMapper, DataBlockMapper dataBlockMapper,
-                                  FileUrlService fileUrlService, FileStoreService fileStoreService) {
-        this.fileMetaMapper = fileMetaMapper;
-        this.dataBlockMapper = dataBlockMapper;
-        this.fileUrlService = fileUrlService;
-        this.fileStoreService = fileStoreService;
-    }
-
-    public synchronized UploadPrepareRet prepareUpload(UploadPrepare uploadPrepare) {
-        String sha256sum = uploadPrepare.getSha256sum();
-        FileMeta fileMeta = fileMetaMapper.findBySha256sum(sha256sum);
-        if (fileMeta == null) {
-            return new UploadPrepareRet("uploadId", PART_SIZE, false, null);
-        } else {
-            String objectName = fileMeta.getObjectName();
-            String url = String.format("https://file.reghao.cn/%s", objectName);
-            return new UploadPrepareRet("uploadId", PART_SIZE, false, url);
-        }
-    }
-
-    /**
-     * 处理通过 HTTP 请求提交的分片文件
-     *
-     * @param
-     * @return
-     * @date 2023-05-19 10:27:01
-     */
-    @Transactional(rollbackFor = Exception.class)
-    public synchronized FilePartRet putFilePart(UploadFilePart uploadFilePart) throws Exception {
-        String partSha256sum = uploadFilePart.getPartSha256sum();
-        long totalSize = uploadFilePart.getTotalSize();
-        long chunkSize = uploadFilePart.getChunkSize();
-        int currentPartSize = uploadFilePart.getCurrentChunkSize();
-        int totalParts = uploadFilePart.getTotalChunks();
-        int chunkNumber = uploadFilePart.getChunkNumber();
-        MultipartFile multipartFile = uploadFilePart.getFile();
-        log.info("{} -> {}:{}", currentPartSize, totalParts, chunkNumber);
-
-        String sha256sum = uploadFilePart.getIdentifier();
-        Set<Integer> set = map.computeIfAbsent(sha256sum, k -> new HashSet<>());
-        if (set.isEmpty()) {
-            String absolutePath = fileUrlService.genFilePath(sha256sum, totalSize);
-            fileStoreService.createSparseFile(absolutePath, totalSize);
-
-            PathUrl pathUrl = new PathUrl(sha256sum, absolutePath);
-            pathMap.put(sha256sum, pathUrl);
-        }
-
-        if (set.add(chunkNumber)) {
-            long pos = (chunkNumber-1) * chunkSize;
-            String absolutePath = pathMap.get(sha256sum).getAbsolutePath();
-            fileStoreService.writeToFile(multipartFile.getInputStream(), absolutePath, pos);
-        }
-
-        if (set.size() != totalParts) {
-            return new FilePartRet(sha256sum);
-        } else {
-            String absolutePath = pathMap.get(sha256sum).getAbsolutePath();
-            FileInputStream fis = new FileInputStream(absolutePath);
-            String sha256sumMerged = DigestUtil.sha256sum(fis);
-            if (!sha256sum.equals(sha256sumMerged)) {
-                throw new Exception("分片合并文件的 sha256sum 与原文件不一致!");
-            }
-
-            String objectName1;
-            String pid = uploadFilePart.getKey();
-            if (pid.equals("0")) {
-                objectName1 = UUID.randomUUID().toString().replace("-", "");
-            } else {
-                FileMeta fileMeta = fileMetaMapper.findByObjectId(pid);
-                String objectName = fileMeta.getObjectName();
-                objectName1 = objectName + UUID.randomUUID().toString().replace("-", "");
-            }
-
-            String filename = uploadFilePart.getFilename();
-            saveFile(objectName1, filename, totalSize, sha256sumMerged, pid, absolutePath);
-            log.info("合并的文件 {}: {}", objectName1, absolutePath);
-            map.remove(sha256sum);
-            pathMap.remove(sha256sum);
-            String url = String.format("https://file.reghao.cn/%s", pid);
-            return new FilePartRet(sha256sum, url);
-        }
-    }
-
-    private void saveFile(String objectName, String filename, long totalSize, String sha256sumMerged,
-                          String pid, String absolutePath) {
-        String objectId = UUID.randomUUID().toString().replace("-", "");
-        String contentType = getMediaType(absolutePath);
-        int fileType = getFileType(contentType);
-
-        FileMeta fileMeta = new FileMeta(objectName, objectId, filename, totalSize, fileType, contentType, sha256sumMerged, pid);
-        List<DataBlock> blocks = new ArrayList<>();
-        String blockId = UUID.randomUUID().toString();
-        blocks.add(new DataBlock(sha256sumMerged, 0, blockId, absolutePath));
-
-        fileMetaMapper.save(fileMeta);
-        dataBlockMapper.saveAll(blocks);
-    }
-
-    private String getMediaType(String src) {
-        String cmd = String.format("/bin/file -b --mime-type \"%s\"", src);
-        return Shell.execWithResult(cmd);
-    }
-
-    private Integer getFileType(String contentType) {
-        int fileType = 1005;
-        if (contentType == null) {
-            return fileType;
-        } else if (contentType.startsWith("image")) {
-            fileType = 1001;
-        } else if (contentType.startsWith("video")) {
-            fileType = 1002;
-        } else if (contentType.startsWith("audio")) {
-            fileType = 1003;
-        } else if (contentType.startsWith("text")) {
-            fileType = 1004;
-        }
-        return fileType;
-    }
-}

+ 133 - 6
dfs-store/src/main/java/cn/reghao/dfs/store/service/ObjectMultipartUploadService.java

@@ -1,25 +1,152 @@
 package cn.reghao.dfs.store.service;
 
+import cn.reghao.dfs.store.db.mapper.DataBlockMapper;
+import cn.reghao.dfs.store.db.mapper.FileMetaMapper;
+import cn.reghao.dfs.store.model.dto.*;
+import cn.reghao.dfs.store.model.po.*;
+import cn.reghao.jutil.jdk.security.DigestUtil;
+import cn.reghao.jutil.jdk.shell.Shell;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.util.*;
 
 /**
  * @author reghao
- * @date 2022-12-08 20:44:16
+ * @date 2023-05-19 10:09:01
  */
+@Slf4j
 @Service
 public class ObjectMultipartUploadService {
-    public void initiateMultipartUpload() {
+    // 20MiB
+    private final static long PART_SIZE = 1024*1024*20;
+    private final FileMetaMapper fileMetaMapper;
+    private final DataBlockMapper dataBlockMapper;
+    private final FileUrlService fileUrlService;
+    private final FileStoreService fileStoreService;
+    private final Map<String, Set<Integer>> map = new HashMap<>();
+    private final Map<String, PathUrl> pathMap = new HashMap<>();
+
+    public ObjectMultipartUploadService(FileMetaMapper fileMetaMapper, DataBlockMapper dataBlockMapper,
+                                        FileUrlService fileUrlService, FileStoreService fileStoreService) {
+        this.fileMetaMapper = fileMetaMapper;
+        this.dataBlockMapper = dataBlockMapper;
+        this.fileUrlService = fileUrlService;
+        this.fileStoreService = fileStoreService;
+    }
+
+    public synchronized UploadPrepareRet prepareUpload(UploadPrepare uploadPrepare) {
+        String sha256sum = uploadPrepare.getSha256sum();
+        FileMeta fileMeta = fileMetaMapper.findBySha256sum(sha256sum);
+        if (fileMeta == null) {
+            return new UploadPrepareRet("uploadId", PART_SIZE, false, null);
+        } else {
+            String objectName = fileMeta.getObjectName();
+            String url = String.format("https://file.reghao.cn/%s", objectName);
+            return new UploadPrepareRet("uploadId", PART_SIZE, false, url);
+        }
     }
 
-    public void uploadPart() {
+    /**
+     * 处理通过 HTTP 请求提交的分片文件
+     *
+     * @param
+     * @return
+     * @date 2023-05-19 10:27:01
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public synchronized FilePartRet putFilePart(UploadFilePart uploadFilePart) throws Exception {
+        String partSha256sum = uploadFilePart.getPartSha256sum();
+        long totalSize = uploadFilePart.getTotalSize();
+        long chunkSize = uploadFilePart.getChunkSize();
+        int currentPartSize = uploadFilePart.getCurrentChunkSize();
+        int totalParts = uploadFilePart.getTotalChunks();
+        int chunkNumber = uploadFilePart.getChunkNumber();
+        MultipartFile multipartFile = uploadFilePart.getFile();
+        log.info("{} -> {}:{}", currentPartSize, totalParts, chunkNumber);
+
+        String sha256sum = uploadFilePart.getIdentifier();
+        Set<Integer> set = map.computeIfAbsent(sha256sum, k -> new HashSet<>());
+        if (set.isEmpty()) {
+            String absolutePath = fileUrlService.genFilePath(sha256sum, totalSize);
+            fileStoreService.createSparseFile(absolutePath, totalSize);
+
+            PathUrl pathUrl = new PathUrl(sha256sum, absolutePath);
+            pathMap.put(sha256sum, pathUrl);
+        }
+
+        if (set.add(chunkNumber)) {
+            long pos = (chunkNumber-1) * chunkSize;
+            String absolutePath = pathMap.get(sha256sum).getAbsolutePath();
+            fileStoreService.writeToFile(multipartFile.getInputStream(), absolutePath, pos);
+        }
+
+        if (set.size() != totalParts) {
+            return new FilePartRet(sha256sum);
+        } else {
+            String absolutePath = pathMap.get(sha256sum).getAbsolutePath();
+            FileInputStream fis = new FileInputStream(absolutePath);
+            String sha256sumMerged = DigestUtil.sha256sum(fis);
+            if (!sha256sum.equals(sha256sumMerged)) {
+                throw new Exception("分片合并文件的 sha256sum 与原文件不一致!");
+            }
+
+            String objectName1;
+            String pid = uploadFilePart.getKey();
+            if (pid.equals("0")) {
+                objectName1 = UUID.randomUUID().toString().replace("-", "");
+            } else {
+                FileMeta fileMeta = fileMetaMapper.findByObjectId(pid);
+                String objectName = fileMeta.getObjectName();
+                objectName1 = objectName + UUID.randomUUID().toString().replace("-", "");
+            }
+
+            String filename = uploadFilePart.getFilename();
+            saveFile(objectName1, filename, totalSize, sha256sumMerged, pid, absolutePath);
+            log.info("合并的文件 {}: {}", objectName1, absolutePath);
+            map.remove(sha256sum);
+            pathMap.remove(sha256sum);
+            String url = String.format("https://file.reghao.cn/%s", pid);
+            return new FilePartRet(sha256sum, url);
+        }
     }
 
-    public void completeMultipartUpload() {
+    private void saveFile(String objectName, String filename, long totalSize, String sha256sumMerged,
+                          String pid, String absolutePath) {
+        String objectId = UUID.randomUUID().toString().replace("-", "");
+        String contentType = getMediaType(absolutePath);
+        int fileType = getFileType(contentType);
+
+        FileMeta fileMeta = new FileMeta(objectName, objectId, filename, totalSize, fileType, contentType, sha256sumMerged, pid);
+        List<DataBlock> blocks = new ArrayList<>();
+        String blockId = UUID.randomUUID().toString();
+        blocks.add(new DataBlock(sha256sumMerged, 0, blockId, absolutePath));
+
+        fileMetaMapper.save(fileMeta);
+        dataBlockMapper.saveAll(blocks);
     }
 
-    public void abortMultipartUpload() {
+    private String getMediaType(String src) {
+        String cmd = String.format("/bin/file -b --mime-type \"%s\"", src);
+        return Shell.execWithResult(cmd);
     }
 
-    public void listParts() {
+    private Integer getFileType(String contentType) {
+        int fileType = 1005;
+        if (contentType == null) {
+            return fileType;
+        } else if (contentType.startsWith("image")) {
+            fileType = 1001;
+        } else if (contentType.startsWith("video")) {
+            fileType = 1002;
+        } else if (contentType.startsWith("audio")) {
+            fileType = 1003;
+        } else if (contentType.startsWith("text")) {
+            fileType = 1004;
+        }
+        return fileType;
     }
 }

+ 97 - 0
dfs-store/src/main/java/cn/reghao/dfs/store/task/ImageFileService.java

@@ -0,0 +1,97 @@
+package cn.reghao.dfs.store.task;
+
+import cn.reghao.dfs.api.dto.ImageFileRet;
+import cn.reghao.dfs.store.db.mapper.ImageFileMapper;
+import cn.reghao.dfs.store.model.po.ImageFile;
+import cn.reghao.dfs.store.util.StringUtil;
+import cn.reghao.dfs.store.util.media.ImageOps;
+import cn.reghao.jutil.jdk.exception.ExceptionUtil;
+import cn.reghao.jutil.tool.id.IdGenerator;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.security.NoSuchAlgorithmException;
+import java.util.UUID;
+
+/**
+ * @author reghao
+ * @date 2021-08-04 17:29:47
+ */
+@Slf4j
+@Service
+public class ImageFileService {
+    private final ImageFileMapper imageFileMapper;
+    private final IdGenerator idGenerator;
+
+    public ImageFileService(ImageFileMapper imageFileMapper) {
+        this.imageFileMapper = imageFileMapper;
+        this.idGenerator = new IdGenerator("image-file-id");
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public synchronized ImageFileRet putFile(MultipartFile multipartFile, String keyPrefix) throws Exception {
+        String contentType = multipartFile.getContentType();
+        String filename = multipartFile.getOriginalFilename();
+        String suffix = StringUtil.getSuffix(filename);
+        byte[] bytes = multipartFile.getBytes();
+
+        String originalUrl = putImageFile(keyPrefix, bytes, suffix);
+        String webpUrl = getWebpUrl(bytes, keyPrefix);
+        if (webpUrl == null) {
+            throw new Exception("webp 图片生成失败");
+        }
+
+        ImageOps.Size size = ImageOps.info(new ByteArrayInputStream(bytes));
+        boolean horizontal = size.getWidth() > size.getHeight();
+        String imageFileId = idGenerator.stringId();
+        ImageFile imageFile = new ImageFile(imageFileId, size.getWidth(), size.getHeight(), horizontal, originalUrl, webpUrl);
+        imageFileMapper.save(imageFile);
+        return new ImageFileRet(imageFileId, webpUrl);
+    }
+
+    private String getWebpUrl(byte[] bytes, String keyPrefix) {
+        try {
+            BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ImageIO.write(bi, "webp", baos);
+            String contentType = "image/webp";
+            return putImageFile(keyPrefix, baos.toByteArray(), "webp");
+        } catch (Exception e) {
+            String stackTrace = ExceptionUtil.stackTrace(e);
+            log.error("{}", stackTrace);
+        }
+
+        return null;
+    }
+
+    private String putImageFile(String keyPrefix, byte[] bytes, String suffix) throws NoSuchAlgorithmException, IOException {
+        String imageId = idGenerator.stringId();
+        String objectKey = String.format("%s%s.%s", keyPrefix, imageId, suffix);
+        String tmpPath = "/opt/tmp/tomcat/" + UUID.randomUUID().toString().replace("-", "");
+        saveFile(tmpPath, bytes);
+        File file = new File(tmpPath);
+        return String.format("//file.reghao.cn/%s", objectKey);
+    }
+
+    private void saveFile(String absolutePath, byte[] bytes) throws IOException {
+        File file = new File(absolutePath);
+        if (file.exists()) {
+            return;
+        }
+
+        File parentDir = file.getParentFile();
+        if (!parentDir.exists()) {
+            FileUtils.forceMkdir(parentDir);
+        }
+
+        FileOutputStream fos = new FileOutputStream(file);
+        fos.write(bytes);
+        fos.close();
+    }
+}

+ 178 - 0
dfs-store/src/main/java/cn/reghao/dfs/store/util/media/ImageOps.java

@@ -0,0 +1,178 @@
+package cn.reghao.dfs.store.util.media;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 对图像的操作
+ *
+ * @author reghao
+ * @date 2021-08-04 16:26:13
+ */
+public class ImageOps {
+    public static String getFormat(File file) {
+        try (ImageInputStream iis = ImageIO.createImageInputStream(file);) {
+            Iterator<ImageReader> iterator = ImageIO.getImageReaders(iis);
+            if ((iterator.hasNext())) {
+                ImageReader ir = iterator.next();
+                return ir.getFormatName();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    public static byte[] convert2jpg(File srcFile) {
+        try (ImageInputStream iis = ImageIO.createImageInputStream(srcFile)) {
+            BufferedImage image = ImageIO.read(iis);
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
+            ImageIO.write(image, "jpg", ios);
+            return baos.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new byte[0];
+    }
+
+    public static byte[] png2jpg(File srcFile) {
+        try {
+            BufferedImage image = ImageIO.read(srcFile);
+            BufferedImage result = new BufferedImage(
+                    image.getWidth(),
+                    image.getHeight(),
+                    BufferedImage.TYPE_INT_RGB);
+            result.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ImageIO.write(result, "jpg", baos);
+            return baos.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new byte[0];
+    }
+
+    public static Size info(InputStream in) throws IOException {
+        BufferedImage bi = ImageIO.read(in);
+        return new Size(bi.getWidth(), bi.getHeight());
+    }
+
+    public static byte[] jpg2webp(byte[] bytes) throws IOException {
+        BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ImageIO.write(bi, "webp", baos);
+        return baos.toByteArray();
+    }
+
+    public static Size info(File file) throws IOException {
+        BufferedImage bi = ImageIO.read(file);
+        return new Size(bi.getWidth(), bi.getHeight());
+    }
+
+    public static BufferedImage merge(List<BufferedImage> bufferedImages, boolean isVertical) {
+        int size = bufferedImages.size();
+        int[][] imageArray = new int[size][];
+        for (int i = 0; i < size; i++) {
+            int width = bufferedImages.get(i).getWidth();
+            int height = bufferedImages.get(i).getHeight();
+            imageArray[i] = new int[width*height];
+            imageArray[i] = bufferedImages.get(i).getRGB(0, 0, width, height, imageArray[i], 0, width);
+        }
+
+        int newHeight = 0, newWidth = 0;
+        for (BufferedImage bufferedImage : bufferedImages) {
+            if (!isVertical) {
+                // 横向拼接,height 不变,width 增加
+                newHeight = Math.max(newHeight, bufferedImage.getHeight());
+                newWidth += bufferedImage.getWidth();
+            } else {
+                // 纵向拼接,width 不变,height 增加
+                newWidth = Math.max(newWidth, bufferedImage.getWidth());
+                newHeight += bufferedImage.getHeight();
+            }
+        }
+
+        BufferedImage newImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
+        int width = 0, height = 0;
+        for (int i = 0; i < size; i++) {
+            if (!isVertical) {
+                // 横向拼接
+                newImage.setRGB(width, 0, bufferedImages.get(i).getWidth(), newHeight, imageArray[i], 0, bufferedImages.get(i).getWidth());
+                width += bufferedImages.get(i).getWidth();
+            } else {
+                // 纵向拼接
+                newImage.setRGB(0, height, newWidth, bufferedImages.get(i).getHeight(), imageArray[i], 0, newWidth);
+                height += bufferedImages.get(i).getHeight();
+            }
+        }
+
+        return newImage;
+    }
+
+    /**
+     * 缩小图片
+     *
+     * @param
+     * @return
+     * @date 2021-08-18 下午1:45
+     */
+    public static BufferedImage resize(BufferedImage srcImage, int size) {
+        int width = srcImage.getWidth()/size;
+        int height = srcImage.getHeight()/size;
+
+        BufferedImage newImage = new BufferedImage(width, height, srcImage.getType());
+        Graphics g = newImage.getGraphics();
+        g.drawImage(srcImage, 0, 0, width, height, null);
+        g.dispose();
+        return newImage;
+    }
+
+    public static BufferedImage resize(String imagePath, int size) throws IOException {
+        BufferedImage srcImage = ImageIO.read(new File(imagePath));
+        int width = srcImage.getWidth()/size;
+        int height = srcImage.getHeight()/size;
+
+        BufferedImage newImage = new BufferedImage(width, height, srcImage.getType());
+        Graphics g = newImage.getGraphics();
+        g.drawImage(srcImage, 0, 0, width, height, null);
+        g.dispose();
+        return newImage;
+    }
+
+    public static void saveImage(BufferedImage bufferedImage, String filePath) throws IOException {
+        ImageIO.write(bufferedImage, "jpg", new File(filePath));
+    }
+
+    public static void saveImage(ByteArrayOutputStream baos, String filePath) throws IOException {
+        BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(baos.toByteArray()));
+        ImageIO.write(bufferedImage, "jpg", new File(filePath));
+    }
+
+    public static class Size {
+        private final int width;
+        private final int height;
+
+        public Size(int width, int height) {
+            this.width = width;
+            this.height = height;
+        }
+
+        public int getWidth() {
+            return width;
+        }
+
+        public int getHeight() {
+            return height;
+        }
+    }
+}

+ 0 - 1
dfs-store/src/test/java/FileMetaTest.java

@@ -4,7 +4,6 @@ import cn.reghao.dfs.store.db.mapper.FileMetaMapper;
 import cn.reghao.dfs.store.model.po.DataBlock;
 import cn.reghao.dfs.store.model.po.FileMeta;
 import cn.reghao.dfs.store.model.vo.ObjectMeta;
-import cn.reghao.dfs.store.redis.ds.RedisStringObj;
 import cn.reghao.jutil.jdk.db.Page;
 import cn.reghao.jutil.jdk.security.DigestUtil;
 import lombok.extern.slf4j.Slf4j;