|
|
@@ -1,15 +1,17 @@
|
|
|
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.dto.PathUrl;
|
|
|
+import cn.reghao.dfs.store.util.ObjectUtil;
|
|
|
+import cn.reghao.oss.common.UploadFilePart;
|
|
|
+import cn.reghao.oss.common.UploadPrepare;
|
|
|
+import cn.reghao.oss.common.UploadPrepareRet;
|
|
|
+import cn.reghao.oss.common.UploadFileRet;
|
|
|
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.*;
|
|
|
@@ -24,16 +26,16 @@ public class ObjectMultipartUploadService {
|
|
|
// 20MiB
|
|
|
private final static long PART_SIZE = 1024*1024*20;
|
|
|
private final FileMetaMapper fileMetaMapper;
|
|
|
- private final DataBlockMapper dataBlockMapper;
|
|
|
private final FileStoreService fileStoreService;
|
|
|
- private final Map<String, Set<Integer>> map = new HashMap<>();
|
|
|
+ private final Map<String, Set<Long>> map = new HashMap<>();
|
|
|
private final Map<String, PathUrl> pathMap = new HashMap<>();
|
|
|
+ private final PutObjectService putObjectService;
|
|
|
|
|
|
- public ObjectMultipartUploadService(FileMetaMapper fileMetaMapper, DataBlockMapper dataBlockMapper,
|
|
|
- FileStoreService fileStoreService) {
|
|
|
+ public ObjectMultipartUploadService(FileMetaMapper fileMetaMapper, FileStoreService fileStoreService,
|
|
|
+ PutObjectService putObjectService) {
|
|
|
this.fileMetaMapper = fileMetaMapper;
|
|
|
- this.dataBlockMapper = dataBlockMapper;
|
|
|
this.fileStoreService = fileStoreService;
|
|
|
+ this.putObjectService = putObjectService;
|
|
|
}
|
|
|
|
|
|
public synchronized UploadPrepareRet prepareUpload(UploadPrepare uploadPrepare) {
|
|
|
@@ -56,96 +58,63 @@ public class ObjectMultipartUploadService {
|
|
|
* @date 2023-05-19 10:27:01
|
|
|
*/
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public synchronized UploadFileRet putFilePart(UploadFilePart uploadFilePart) throws Exception {
|
|
|
- String partSha256sum = uploadFilePart.getPartSha256sum();
|
|
|
+ public synchronized UploadFileRet putFilePart(InputStream inputStream, UploadFilePart uploadFilePart) throws Exception {
|
|
|
+ String filename = uploadFilePart.getFilename();
|
|
|
long totalSize = uploadFilePart.getTotalSize();
|
|
|
long chunkSize = uploadFilePart.getChunkSize();
|
|
|
int currentPartSize = uploadFilePart.getCurrentChunkSize();
|
|
|
- int totalParts = uploadFilePart.getTotalChunks();
|
|
|
- int chunkNumber = uploadFilePart.getChunkNumber();
|
|
|
- MultipartFile multipartFile = uploadFilePart.getFile();
|
|
|
+ long totalParts = uploadFilePart.getTotalChunks();
|
|
|
+ long chunkNumber = uploadFilePart.getChunkNumber();
|
|
|
log.info("{} -> {}:{}", currentPartSize, totalParts, chunkNumber);
|
|
|
|
|
|
String sha256sum = uploadFilePart.getIdentifier();
|
|
|
- Set<Integer> set = map.computeIfAbsent(sha256sum, k -> new HashSet<>());
|
|
|
+ FileMeta fileMeta = fileMetaMapper.findBySha256sum(sha256sum);
|
|
|
+ if (fileMeta != null) {
|
|
|
+ String objectName = ObjectUtil.getObjectName(uploadFilePart.getChannelId());
|
|
|
+ putObjectService.copyObject(objectName, filename, fileMeta);
|
|
|
+ String url = String.format("https://file.reghao.cn/%s", objectName);
|
|
|
+ return new UploadFileRet(sha256sum, url);
|
|
|
+ }
|
|
|
+
|
|
|
+ Set<Long> set = map.computeIfAbsent(sha256sum, k -> new HashSet<>());
|
|
|
if (set.isEmpty()) {
|
|
|
- String absolutePath = fileStoreService.genFilePath(sha256sum, totalSize);
|
|
|
+ String contentId = UUID.randomUUID().toString().replace("-", "");
|
|
|
+ String absolutePath = fileStoreService.genFilePath(contentId, totalSize);
|
|
|
fileStoreService.createSparseFile(absolutePath, totalSize);
|
|
|
|
|
|
- PathUrl pathUrl = new PathUrl(sha256sum, absolutePath);
|
|
|
+ PathUrl pathUrl = new PathUrl(contentId, 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);
|
|
|
+ fileStoreService.writeToFile(inputStream, absolutePath, pos);
|
|
|
}
|
|
|
|
|
|
if (set.size() != totalParts) {
|
|
|
return new UploadFileRet(sha256sum);
|
|
|
} else {
|
|
|
+ String contentId = pathMap.get(sha256sum).getContentId();
|
|
|
String absolutePath = pathMap.get(sha256sum).getAbsolutePath();
|
|
|
FileInputStream fis = new FileInputStream(absolutePath);
|
|
|
String sha256sumMerged = DigestUtil.sha256sum(fis);
|
|
|
if (!sha256sum.equals(sha256sumMerged)) {
|
|
|
throw new Exception("分片合并文件的 sha256sum 与原文件不一致!");
|
|
|
+ } else {
|
|
|
+ log.info("合并的文件 {}", absolutePath);
|
|
|
}
|
|
|
|
|
|
- String objectName1;
|
|
|
int channelId = uploadFilePart.getChannelId();
|
|
|
String pid = uploadFilePart.getPid();
|
|
|
- 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 objectName = ObjectUtil.getObjectName(channelId);
|
|
|
+ File savedFile = new File(absolutePath);
|
|
|
+ putObjectService.putObject(pid, objectName, contentId, savedFile, filename, absolutePath);
|
|
|
|
|
|
- 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);
|
|
|
+ String url = String.format("https://file.reghao.cn/%s", objectName);
|
|
|
return new UploadFileRet(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;
|
|
|
- }
|
|
|
}
|