فهرست منبع

oss-store 大文件分片上传接口实现

reghao 1 سال پیش
والد
کامیت
28a94d88e0

+ 2 - 2
oss-api/src/main/java/cn/reghao/oss/api/rest/UploadFilePart.java

@@ -26,9 +26,9 @@ public class UploadFilePart implements Serializable {
     // 分片文件大小
     private Integer chunkSize;
     // 分片文件数量
-    private Long totalChunks;
+    private Integer totalChunks;
     // 当前分片文件索引
-    private Long chunkNumber;
+    private Integer chunkNumber;
     // 当前分片文件大小
     private Integer currentChunkSize;
 }

+ 5 - 1
oss-api/src/main/java/cn/reghao/oss/api/rest/UploadPrepare.java

@@ -18,10 +18,14 @@ import java.io.Serializable;
 public class UploadPrepare implements Serializable {
     private static final long serialVersionUID = 1L;
 
+    @NotNull
+    private int channelId;
     @NotBlank
     private String filename;
     @NotNull
-    private Long size;
+    private long size;
     @NotBlank
     private String sha256sum;
+    @NotBlank
+    private String contentType;
 }

+ 117 - 16
oss-store/src/main/java/cn/reghao/oss/store/controller/ObjectMultipartUploadController.java

@@ -1,17 +1,24 @@
 package cn.reghao.oss.store.controller;
 
+import cn.reghao.jutil.web.ServletUtil;
+import cn.reghao.oss.api.dto.ObjectChannel;
+import cn.reghao.oss.api.dto.OssPayload;
+import cn.reghao.oss.api.iface.ConsoleService;
+import cn.reghao.oss.api.util.AuthContext;
+import cn.reghao.oss.api.util.JwtUtil;
 import cn.reghao.oss.store.service.ObjectMultipartUploadService;
 import cn.reghao.jutil.jdk.result.WebResult;
 import cn.reghao.oss.api.rest.*;
+import com.github.benmanes.caffeine.cache.Cache;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.util.*;
-
 /**
  * @author reghao
  * @date 2022-12-08 20:40:55
@@ -20,34 +27,128 @@ import java.util.*;
 @RestController
 public class ObjectMultipartUploadController {
     private final ObjectMultipartUploadService objectMultipartUploadService;
+    private final ConsoleService consoleService;
+    private final Cache<String, String> cache;
 
-    public ObjectMultipartUploadController(ObjectMultipartUploadService objectMultipartUploadService) {
+    public ObjectMultipartUploadController(ObjectMultipartUploadService objectMultipartUploadService,
+                                           ConsoleService consoleService, Cache<String, String> cache) {
         this.objectMultipartUploadService = objectMultipartUploadService;
+        this.consoleService = consoleService;
+        this.cache = cache;
     }
 
     @ApiOperation(value = "创建对象分片上传")
     @PostMapping(value = "/", params = {"create"}, produces = MediaType.APPLICATION_JSON_VALUE)
-    public String create(@Validated UploadPrepare uploadPrepare) {
-        UploadPrepareRet uploadPrepareRet = objectMultipartUploadService.prepareUpload(uploadPrepare);
-        return WebResult.success(uploadPrepareRet);
+    public ResponseEntity<String> create(@Validated UploadPrepare uploadPrepare) throws Exception {
+        int channelId = uploadPrepare.getChannelId();
+        /* permission check */
+        String token = ServletUtil.getBearerToken();
+        if (token == null) {
+            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+                    .body(WebResult.failWithMsg("no token in request"));
+        }
+
+        String secretKey = cache.getIfPresent(token);
+        OssPayload ossPayload = JwtUtil.getOssPayload(token, secretKey);
+        String action = ossPayload.getAction();
+        if (!"upload".equals(action)) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg("it's not upload token"));
+        }
+
+        int channelId1 = ossPayload.getChannelId();
+        if (channelId != channelId1) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg("channel not match in token"));
+        }
+
+        int loginUser = ossPayload.getUserId();
+        ObjectChannel objectChannel = consoleService.getChannelById(loginUser, channelId);
+        if (objectChannel == null) {
+            String errMsg = String.format("channel validate failed, channel %s not exist", channelId);
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg(errMsg));
+        }
+        AuthContext context = new AuthContext(loginUser);
+
+        UploadPrepareRet uploadPrepareRet = objectMultipartUploadService.prepareUpload(uploadPrepare, objectChannel);
+        return ResponseEntity.status(HttpStatus.OK).body(WebResult.success(uploadPrepareRet));
     }
 
     @ApiOperation(value = "获取已上传的对象分片")
     @GetMapping(value = "/", params = {"multipart"}, produces = MediaType.APPLICATION_JSON_VALUE)
-    public String getUploadedPart() {
-        UploadedPart uploadedPart = new UploadedPart();
-        uploadedPart.setSkipUpload(false);
-        uploadedPart.setNeedMerge(false);
-        uploadedPart.setUrl("");
-        uploadedPart.setUploaded(Collections.emptyList());
-        return WebResult.success(uploadedPart);
+    public ResponseEntity<String> getUploadedPart() {
+        int channelId = 1;
+        /* permission check */
+        String token = ServletUtil.getBearerToken();
+        if (token == null) {
+            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+                    .body(WebResult.failWithMsg("no token in request"));
+        }
+
+        String secretKey = cache.getIfPresent(token);
+        OssPayload ossPayload = JwtUtil.getOssPayload(token, secretKey);
+        String action = ossPayload.getAction();
+        if (!"upload".equals(action)) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg("it's not upload token"));
+        }
+
+        int channelId1 = ossPayload.getChannelId();
+        if (channelId != channelId1) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg("channel not match in token"));
+        }
+
+        int loginUser = ossPayload.getUserId();
+        ObjectChannel objectChannel = consoleService.getChannelById(loginUser, channelId);
+        if (objectChannel == null) {
+            String errMsg = String.format("channel validate failed, channel %s not exist", channelId);
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg(errMsg));
+        }
+        AuthContext context = new AuthContext(loginUser);
+
+        UploadedPart uploadedPart = objectMultipartUploadService.getUploadedPart();
+        return ResponseEntity.status(HttpStatus.OK).body(WebResult.success(uploadedPart));
     }
 
     @ApiOperation(value = "上传对象分片")
     @PostMapping(value = "/", params = {"multipart"}, produces = MediaType.APPLICATION_JSON_VALUE)
-    public String uploadPart(MultipartFile file, @Validated UploadFilePart uploadFilePart) throws Exception {
-        UploadFileRet uploadFileRet = objectMultipartUploadService.putFilePart(file.getInputStream(), uploadFilePart);
-        return WebResult.success(uploadFileRet);
+    public ResponseEntity<String> uploadPart(MultipartFile file, @Validated UploadFilePart uploadFilePart) throws Exception {
+        int channelId = uploadFilePart.getChannelId();
+        /* permission check */
+        String token = ServletUtil.getBearerToken();
+        if (token == null) {
+            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+                    .body(WebResult.failWithMsg("no token in request"));
+        }
+
+        String secretKey = cache.getIfPresent(token);
+        OssPayload ossPayload = JwtUtil.getOssPayload(token, secretKey);
+        String action = ossPayload.getAction();
+        if (!"upload".equals(action)) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg("it's not upload token"));
+        }
+
+        int channelId1 = ossPayload.getChannelId();
+        if (channelId != channelId1) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg("channel not match in token"));
+        }
+
+        int loginUser = ossPayload.getUserId();
+        ObjectChannel objectChannel = consoleService.getChannelById(loginUser, channelId);
+        if (objectChannel == null) {
+            String errMsg = String.format("channel validate failed, channel %s not exist", channelId);
+            return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                    .body(WebResult.failWithMsg(errMsg));
+        }
+        AuthContext context = new AuthContext(loginUser);
+
+        UploadFileRet uploadFileRet = objectMultipartUploadService.putFilePart(file.getInputStream(), uploadFilePart, objectChannel);
+        return ResponseEntity.status(HttpStatus.OK).body(WebResult.success(uploadFileRet));
     }
 
     @ApiOperation(value = "合并对象分片")

+ 1 - 3
oss-store/src/main/java/cn/reghao/oss/store/controller/ObjectUploadController.java

@@ -89,9 +89,7 @@ public class ObjectUploadController {
             return ResponseEntity.status(HttpStatus.FORBIDDEN)
                     .body(WebResult.failWithMsg(errMsg));
         }
-
-        int userId1 = ossPayload.getUserId();
-        AuthContext context = new AuthContext(userId1);
+        AuthContext context = new AuthContext(loginUser);
 
         /* channel validate */
         String contentId = UUID.randomUUID().toString().replace("-", "");

+ 16 - 0
oss-store/src/main/java/cn/reghao/oss/store/db/mapper/FilePartMapper.java

@@ -0,0 +1,16 @@
+package cn.reghao.oss.store.db.mapper;
+
+import cn.reghao.jutil.jdk.db.BaseMapper;
+import cn.reghao.oss.store.model.po.FilePart;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2024-10-24 16:36:13
+ */
+@Mapper
+public interface FilePartMapper extends BaseMapper<FilePart> {
+    List<FilePart> findByObjectId(String objectId);
+}

+ 16 - 0
oss-store/src/main/java/cn/reghao/oss/store/db/mapper/FilePartsMapper.java

@@ -0,0 +1,16 @@
+package cn.reghao.oss.store.db.mapper;
+
+import cn.reghao.jutil.jdk.db.BaseMapper;
+import cn.reghao.oss.store.model.po.FilePart;
+import cn.reghao.oss.store.model.po.FileParts;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author reghao
+ * @date 2024-10-24 16:35:48
+ */
+@Mapper
+public interface FilePartsMapper extends BaseMapper<FileParts> {
+    FilePart findByObjectId(String objectId);
+    FilePart findBySha256sum(String sha256sum);
+}

+ 33 - 0
oss-store/src/main/java/cn/reghao/oss/store/db/repository/FilePartRepository.java

@@ -0,0 +1,33 @@
+package cn.reghao.oss.store.db.repository;
+
+import cn.reghao.oss.store.db.mapper.FilePartMapper;
+import cn.reghao.oss.store.db.mapper.FilePartsMapper;
+import cn.reghao.oss.store.model.po.FilePart;
+import cn.reghao.oss.store.model.po.FileParts;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author reghao
+ * @date 2024-10-24 17:05:20
+ */
+@Service
+public class FilePartRepository {
+    private final FilePartsMapper filePartsMapper;
+    private final FilePartMapper filePartMapper;
+
+    public FilePartRepository(FilePartsMapper filePartsMapper, FilePartMapper filePartMapper) {
+        this.filePartsMapper = filePartsMapper;
+        this.filePartMapper = filePartMapper;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void save(FileParts fileParts, FilePart filePart) {
+        filePartsMapper.save(fileParts);
+        filePartMapper.save(filePart);
+    }
+
+    public void saveFilePart(FilePart filePart) {
+        filePartMapper.save(filePart);
+    }
+}

+ 1 - 4
oss-store/src/main/java/cn/reghao/oss/store/db/repository/ObjectRepository.java

@@ -6,7 +6,6 @@ import cn.reghao.jutil.jdk.converter.DateTimeConverter;
 import cn.reghao.jutil.jdk.db.Page;
 import cn.reghao.oss.api.constant.ObjectType;
 import cn.reghao.oss.api.dto.FileInfo;
-import cn.reghao.oss.api.iface.ConsoleService;
 import cn.reghao.oss.store.db.mapper.DataBlockMapper;
 import cn.reghao.oss.store.db.mapper.FileMetaMapper;
 import cn.reghao.oss.store.model.po.DataBlock;
@@ -19,7 +18,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.File;
-import java.time.LocalDateTime;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -32,8 +30,7 @@ import java.util.stream.Collectors;
 public class ObjectRepository {
     private final FileMetaMapper fileMetaMapper;
     private final DataBlockMapper dataBlockMapper;
-    private ConsoleService consoleService;
-    private ByteConverter byteConverter = new ByteConverter();
+    private final ByteConverter byteConverter = new ByteConverter();
 
     public ObjectRepository(FileMetaMapper fileMetaMapper, DataBlockMapper dataBlockMapper) {
         this.fileMetaMapper = fileMetaMapper;

+ 2 - 2
oss-store/src/main/java/cn/reghao/oss/store/model/po/FileMeta.java

@@ -58,7 +58,7 @@ public class FileMeta extends BaseObject<Integer> {
         this.scope = scope;
     }
 
-    public FileMeta(String objectName, String objectId, String filename, FileMeta fileMeta, int scope) {
+    public FileMeta(String objectName, String objectId, String filename, FileMeta fileMeta, String pid, int scope) {
         this.objectName = objectName;
         this.objectId = objectId;
         this.contentId = fileMeta.getContentId();
@@ -67,7 +67,7 @@ public class FileMeta extends BaseObject<Integer> {
         this.fileType = fileMeta.getFileType();
         this.contentType = fileMeta.getContentType();
         this.sha256sum = fileMeta.getSha256sum();
-        this.pid = fileMeta.getPid();
+        this.pid = pid;
         this.uploadBy = AuthContext.getUserId();
         this.scope = scope;
     }

+ 24 - 0
oss-store/src/main/java/cn/reghao/oss/store/model/po/FilePart.java

@@ -0,0 +1,24 @@
+package cn.reghao.oss.store.model.po;
+
+import cn.reghao.jutil.jdk.db.BaseObject;
+import cn.reghao.oss.api.rest.UploadFilePart;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author reghao
+ * @date 2024-10-24 16:33:24
+ */
+@NoArgsConstructor
+@Getter
+public class FilePart extends BaseObject<Integer> {
+    private String objectId;
+    private Integer chunkNumber;
+    private Integer currentChunkSize;
+
+    public FilePart(String objectId, UploadFilePart uploadFilePart) {
+        this.objectId = objectId;
+        this.chunkNumber = uploadFilePart.getChunkNumber();
+        this.currentChunkSize = uploadFilePart.getCurrentChunkSize();
+    }
+}

+ 32 - 0
oss-store/src/main/java/cn/reghao/oss/store/model/po/FileParts.java

@@ -0,0 +1,32 @@
+package cn.reghao.oss.store.model.po;
+
+import cn.reghao.jutil.jdk.db.BaseObject;
+import cn.reghao.oss.api.rest.UploadFilePart;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author reghao
+ * @date 2024-10-24 16:33:16
+ */
+@NoArgsConstructor
+@Getter
+public class FileParts extends BaseObject<Integer> {
+    private String objectId;
+    private String absolutePath;
+    private String sha256sum;
+    private Long totalSize;
+    private Integer chunkSize;
+    private Integer totalChunks;
+    private Boolean uploaded;
+
+    public FileParts(String objectId, String absolutePath, UploadFilePart uploadFilePart) {
+        this.objectId = objectId;
+        this.absolutePath = absolutePath;
+        this.sha256sum = uploadFilePart.getIdentifier();
+        this.totalSize = uploadFilePart.getTotalSize();
+        this.chunkSize = uploadFilePart.getChunkSize();
+        this.totalChunks = uploadFilePart.getTotalChunks();
+        this.uploaded = false;
+    }
+}

+ 56 - 18
oss-store/src/main/java/cn/reghao/oss/store/service/ObjectMultipartUploadService.java

@@ -2,14 +2,15 @@ package cn.reghao.oss.store.service;
 
 import cn.reghao.jutil.web.ServletUtil;
 import cn.reghao.oss.api.dto.ObjectChannel;
+import cn.reghao.oss.api.rest.*;
+import cn.reghao.oss.store.db.repository.FilePartRepository;
 import cn.reghao.oss.store.db.repository.ObjectRepository;
 import cn.reghao.oss.store.model.dto.PathUrl;
+import cn.reghao.oss.store.model.po.FilePart;
+import cn.reghao.oss.store.model.po.FileParts;
 import cn.reghao.oss.store.model.vo.ObjectProp;
+import cn.reghao.oss.store.util.FileType;
 import cn.reghao.oss.store.util.StringUtil;
-import cn.reghao.oss.api.rest.UploadFilePart;
-import cn.reghao.oss.api.rest.UploadPrepare;
-import cn.reghao.oss.api.rest.UploadPrepareRet;
-import cn.reghao.oss.api.rest.UploadFileRet;
 import cn.reghao.jutil.jdk.security.DigestUtil;
 import cn.reghao.oss.store.model.po.FileMeta;
 import lombok.extern.slf4j.Slf4j;
@@ -34,28 +35,61 @@ public class ObjectMultipartUploadService {
     private final Map<String, PathUrl> pathMap = new HashMap<>();
     private final ObjectNameService objectNameService;
     private final PutObjectService putObjectService;
+    private final FilePartRepository filePartRepository;
 
     public ObjectMultipartUploadService(ObjectRepository objectRepository, FileStoreService fileStoreService,
-                                        ObjectNameService objectNameService, PutObjectService putObjectService) {
+                                        ObjectNameService objectNameService, PutObjectService putObjectService,
+                                        FilePartRepository filePartRepository) {
         this.objectRepository = objectRepository;
         this.fileStoreService = fileStoreService;
         this.objectNameService = objectNameService;
         this.putObjectService = putObjectService;
+        this.filePartRepository = filePartRepository;
     }
 
-    public synchronized UploadPrepareRet prepareUpload(UploadPrepare uploadPrepare) {
+    public synchronized UploadPrepareRet prepareUpload(UploadPrepare uploadPrepare, ObjectChannel objectChannel) throws Exception {
+        String filename = uploadPrepare.getFilename();
         String sha256sum = uploadPrepare.getSha256sum();
+        String contentType = uploadPrepare.getContentType();
+        long size = uploadPrepare.getSize();
         FileMeta fileMeta = objectRepository.getBySha256sum(sha256sum);
         if (fileMeta == null) {
-            return new UploadPrepareRet("uploadId", PART_SIZE, false, null);
+            String suffix = StringUtil.getSuffix(filename);
+            ObjectProp objectProp = objectNameService.getObjectProp(objectChannel, suffix);
+            String pid = objectProp.getPid();
+            String objectName = objectProp.getObjectName();
+            String objectId = UUID.randomUUID().toString().replace("-", "");
+            int fileType = FileType.getFileType(contentType);
+            //int scope = objectProp.getScope();
+            String contentId = UUID.randomUUID().toString().replace("-", "");
+
+            int scope = 0;
+            fileMeta = new FileMeta(objectName, objectId, contentId, filename, size, fileType, contentType, sha256sum, pid, scope);
+            objectRepository.saveFileMeta(fileMeta);
+            return new UploadPrepareRet(objectId, PART_SIZE, false, null);
         } else {
-            String objectName = fileMeta.getObjectName();
-            String host = ServletUtil.getHeader("host");
-            String url = String.format("//%s/%s", host, objectName);
-            return new UploadPrepareRet("uploadId", PART_SIZE, false, url);
+            String suffix = StringUtil.getSuffix(filename);
+            ObjectProp objectProp = objectNameService.getObjectProp(objectChannel, suffix);
+            String pid = objectProp.getPid();
+            String toObjectName = objectProp.getObjectName();
+            String toObjectId = UUID.randomUUID().toString().replace("-", "");
+            int scope = objectProp.getScope();
+
+            FileMeta fileMeta1 = new FileMeta(toObjectName, toObjectId, filename, fileMeta, pid, scope);
+            objectRepository.saveFileMeta(fileMeta1);
+            return new UploadPrepareRet(toObjectId, PART_SIZE, true, null);
         }
     }
 
+    public UploadedPart getUploadedPart() {
+        UploadedPart uploadedPart = new UploadedPart();
+        uploadedPart.setSkipUpload(false);
+        uploadedPart.setNeedMerge(false);
+        uploadedPart.setUrl("");
+        uploadedPart.setUploaded(Collections.emptyList());
+        return uploadedPart;
+    }
+
     /**
      * 处理通过 HTTP 请求提交的分片文件
      *
@@ -64,7 +98,9 @@ public class ObjectMultipartUploadService {
      * @date 2023-05-19 10:27:01
      */
     @Transactional(rollbackFor = Exception.class)
-    public synchronized UploadFileRet putFilePart(InputStream inputStream, UploadFilePart uploadFilePart) throws Exception {
+    public synchronized UploadFileRet putFilePart(InputStream inputStream,
+                                                  UploadFilePart uploadFilePart,
+                                                  ObjectChannel objectChannel) throws Exception {
         String filename = uploadFilePart.getFilename();
         long totalSize = uploadFilePart.getTotalSize();
         long chunkSize = uploadFilePart.getChunkSize();
@@ -78,7 +114,7 @@ public class ObjectMultipartUploadService {
         if (fileMeta != null) {
             String objectName = fileMeta.getObjectName();
             String suffix = StringUtil.getSuffix(objectName);
-            ObjectChannel objectChannel = null;
+
             ObjectProp objectProp = objectNameService.getObjectProp(objectChannel, suffix);
             putObjectService.copyObject(objectProp, filename, fileMeta);
 
@@ -93,6 +129,11 @@ public class ObjectMultipartUploadService {
             String absolutePath = fileStoreService.genFilePath(contentId, totalSize, "");
             fileStoreService.createSparseFile(absolutePath, totalSize);
 
+            String objectId = "";
+            FileParts fileParts = new FileParts(objectId, absolutePath, uploadFilePart);
+            FilePart filePart = new FilePart(objectId, uploadFilePart);
+            filePartRepository.save(fileParts, filePart);
+
             PathUrl pathUrl = new PathUrl(contentId, absolutePath);
             pathMap.put(sha256sum, pathUrl);
         }
@@ -112,16 +153,13 @@ public class ObjectMultipartUploadService {
             String sha256sumMerged = DigestUtil.sha256sum(fis);
             if (!sha256sum.equals(sha256sumMerged)) {
                 throw new Exception("分片合并文件的 sha256sum 与原文件不一致!");
-            } else {
-                log.info("合并的文件 {}", absolutePath);
             }
 
-            int channelId = uploadFilePart.getChannelId();
+            log.info("合并的文件 {}", absolutePath);
             String suffix = StringUtil.getSuffix(filename);
-            ObjectChannel objectChannel = null;
             ObjectProp objectProp = objectNameService.getObjectProp(objectChannel, suffix);
             File savedFile = new File(absolutePath);
-            putObjectService.putObject(objectProp, contentId, savedFile, filename, absolutePath);
+            putObjectService.putObject(objectProp, contentId, savedFile, filename, sha256sum);
 
             map.remove(sha256sum);
             pathMap.remove(sha256sum);

+ 6 - 3
oss-store/src/main/java/cn/reghao/oss/store/service/PutObjectService.java

@@ -83,7 +83,8 @@ public class PutObjectService {
         String objectName = objectProp.getObjectName();
         String objectId = UUID.randomUUID().toString().replace("-", "");
         int scope = objectProp.getScope();
-        FileMeta fileMeta1 = new FileMeta(objectName, objectId, filename, fileMeta, scope);
+        String pid = objectProp.getPid();
+        FileMeta fileMeta1 = new FileMeta(objectName, objectId, filename, fileMeta, pid, scope);
         objectRepository.saveFileMeta(fileMeta1);
         return new ObjectResult(objectName, objectId, fileType, savedPath, dupObjectId);
     }
@@ -100,7 +101,8 @@ public class PutObjectService {
         int scope = fileMeta.getScope();
         String toObjectName = objectNameService.getObjectNameFromOriginal(fromObjectName, suffix);
         String toObjectId = UUID.randomUUID().toString().replace("-", "");
-        FileMeta fileMeta1 = new FileMeta(toObjectName, toObjectId, filename, fileMeta, scope);
+        String pid = "0";
+        FileMeta fileMeta1 = new FileMeta(toObjectName, toObjectId, filename, fileMeta, pid, scope);
         objectRepository.saveFileMeta(fileMeta1);
         return new ObjectResult(toObjectName, toObjectId, fileType, savedPath);
     }
@@ -116,7 +118,8 @@ public class PutObjectService {
         int scope = fileMeta.getScope();
         String toObjectName = objectNameService.getObjectNameFromOriginal(fromObjectName, suffix);
         String toObjectId = UUID.randomUUID().toString().replace("-", "");
-        FileMeta fileMeta1 = new FileMeta(toObjectName, toObjectId, filename, fileMeta, scope);
+        String pid = fileMeta.getPid();
+        FileMeta fileMeta1 = new FileMeta(toObjectName, toObjectId, filename, fileMeta, pid, scope);
         objectRepository.saveFileMeta(fileMeta1);
         return new ObjectResult(toObjectName, toObjectId, fileType, savedPath);
     }

+ 25 - 0
oss-store/src/main/resources/mapper/FilePartMapper.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="cn.reghao.oss.store.db.mapper.FilePartMapper">
+    <insert id="save" useGeneratedKeys="true" keyProperty="id">
+        insert into file_part
+        (`object_id`,`chunk_number`,`current_chunk_size`)
+        values
+        (#{objectId},#{chunkNumber},#{current_chunkSize})
+    </insert>
+    <insert id="saveAll" useGeneratedKeys="true" keyProperty="id">
+        insert into file_part
+        (`object_id`,`chunk_number`,`current_chunk_size`)
+        values
+        <foreach collection="list" item="item" index="index" separator=",">
+            (#{item.objectId},#{item.chunkNumber},#{item.current_chunkSize})
+        </foreach>
+    </insert>
+
+    <select id="findByObjectId" resultType="cn.reghao.oss.store.model.po.FilePart">
+        select *
+        from file_part
+        where object_id=#{objectId}
+    </select>
+</mapper>

+ 22 - 0
oss-store/src/main/resources/mapper/FilePartsMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="cn.reghao.oss.store.db.mapper.FilePartsMapper">
+    <insert id="save" useGeneratedKeys="true" keyProperty="id">
+        insert into file_parts
+        (`object_id`,`absolute_path`,`sha256sum`,`total_size`,`chunk_size`,`total_chunks`,`uploaded`)
+        values
+        (#{objectId},#{absolutePath},#{sha256sum},#{totalSize},#{chunkSize},#{totalChunks},#{uploaded})
+    </insert>
+    
+    <select id="findByObjectId" resultType="cn.reghao.oss.store.model.po.FileParts">
+        select * 
+        from file_parts
+        where object_id=#{objectId}
+    </select>
+    <select id="findBySha256sum" resultType="cn.reghao.oss.store.model.po.FileParts">
+        select *
+        from file_parts
+        where sha256sum=#{sha256sum}
+    </select>
+</mapper>