reghao 3 лет назад
Родитель
Сommit
6ffac29bae

+ 5 - 0
src/main/java/cn/reghao/tnb/file/app/config/DfsProperties.java

@@ -1,5 +1,7 @@
 package cn.reghao.tnb.file.app.config;
 package cn.reghao.tnb.file.app.config;
 
 
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
@@ -9,10 +11,13 @@ import java.util.List;
  * @author reghao
  * @author reghao
  * @date 2021-12-30 11:01:46
  * @date 2021-12-30 11:01:46
  */
  */
+@AllArgsConstructor
+@NoArgsConstructor
 @Component
 @Component
 @ConfigurationProperties(prefix = "dfs")
 @ConfigurationProperties(prefix = "dfs")
 public class DfsProperties {
 public class DfsProperties {
     private String domain;
     private String domain;
+    // TODO 修改为 mountedDirs, 表示磁盘挂载的目录
     private List<String> baseDirs;
     private List<String> baseDirs;
 
 
     public void setDomain(String domain) {
     public void setDomain(String domain) {

+ 9 - 15
src/main/java/cn/reghao/tnb/file/app/controller/FileController.java

@@ -2,35 +2,23 @@ package cn.reghao.tnb.file.app.controller;
 
 
 import cn.reghao.jutil.jdk.result.WebBody;
 import cn.reghao.jutil.jdk.result.WebBody;
 import cn.reghao.tnb.file.app.db.repository.FileRepository;
 import cn.reghao.tnb.file.app.db.repository.FileRepository;
-import cn.reghao.tnb.file.app.service.SpiderFileService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 /**
 /**
  * @author reghao
  * @author reghao
- * @date 2020-03-11 16:10:16
+ * @date 2022-06-20 13:46:12
  */
  */
-@Slf4j
 @Api(tags = "文件服务接口")
 @Api(tags = "文件服务接口")
 @RestController
 @RestController
-@RequestMapping("/api/file")
-@Deprecated
+@RequestMapping("/api/file/")
 public class FileController {
 public class FileController {
     private final FileRepository fileRepository;
     private final FileRepository fileRepository;
-    private SpiderFileService spiderFileService;
 
 
-    public FileController(FileRepository fileRepository, SpiderFileService spiderFileService) {
+    public FileController(FileRepository fileRepository) {
         this.fileRepository = fileRepository;
         this.fileRepository = fileRepository;
-        this.spiderFileService = spiderFileService;
-    }
-
-    @ApiOperation("检查文件是否已存在")
-    @GetMapping("/exist")
-    public String isFileExist(@RequestParam("sha256sum") String sha256sum) {
-        return WebBody.success();
     }
     }
 
 
     @ApiOperation(value = "删除已上传的文件")
     @ApiOperation(value = "删除已上传的文件")
@@ -40,4 +28,10 @@ public class FileController {
         String msg = String.format("%s 已删除", uploadId);
         String msg = String.format("%s 已删除", uploadId);
         return WebBody.success(msg);
         return WebBody.success(msg);
     }
     }
+
+    @ApiOperation("检查文件是否已存在")
+    @GetMapping("/exist")
+    public String isFileExist(@RequestParam("sha256sum") String sha256sum) {
+        return WebBody.success();
+    }
 }
 }

+ 42 - 24
src/main/java/cn/reghao/tnb/file/app/controller/SpiderFileController.java

@@ -1,47 +1,65 @@
 package cn.reghao.tnb.file.app.controller;
 package cn.reghao.tnb.file.app.controller;
 
 
 import cn.reghao.jutil.jdk.result.WebBody;
 import cn.reghao.jutil.jdk.result.WebBody;
+import cn.reghao.tnb.file.app.db.repository.FileRepository;
+import cn.reghao.tnb.file.app.model.dto.UploadFile;
+import cn.reghao.tnb.file.app.model.dto.UploadedFile;
+import cn.reghao.tnb.file.app.model.vo.UploadFileRet;
+import cn.reghao.tnb.file.app.service.FileUploadService;
 import cn.reghao.tnb.file.app.service.SpiderFileService;
 import cn.reghao.tnb.file.app.service.SpiderFileService;
-import cn.reghao.tnb.file.client.model.SpiderFile;
-import cn.reghao.tnb.file.client.model.SpiderImageFile;
-import cn.reghao.tnb.file.client.model.SpiderVideoFile;
+import cn.reghao.tnb.file.app.service.media.ImageFileService;
+import cn.reghao.tnb.file.app.service.media.VideoFileService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.NotNull;
+import java.io.InputStream;
 
 
 /**
 /**
  * @author reghao
  * @author reghao
- * @date 2022-06-16 10:55:25
+ * @date 2020-03-11 16:10:16
  */
  */
-@Api(tags = "文件服务接口")
+@Slf4j
 @RestController
 @RestController
-@RequestMapping("/api/file/spider")
+@RequestMapping("/api/file/upload/spider")
+@Deprecated
 public class SpiderFileController {
 public class SpiderFileController {
-    private final SpiderFileService spiderFileService;
+    private final FileUploadService fileUploadService;
+    private ImageFileService imageService;
+    private VideoFileService videoService;
 
 
-    public SpiderFileController(SpiderFileService spiderFileService) {
-        this.spiderFileService = spiderFileService;
+    public SpiderFileController(FileUploadService fileUploadService, ImageFileService imageService,
+                                 VideoFileService videoService) {
+        this.fileUploadService = fileUploadService;
+        this.imageService = imageService;
+        this.videoService = videoService;
     }
     }
 
 
-    @ApiOperation("添加文件")
-    @PostMapping("/file")
-    public String addFile(@RequestBody @Validated SpiderFile spiderFile) {
-        String url = spiderFileService.addFile(spiderFile);
-        return WebBody.success(url);
-    }
+    @ApiOperation(value = "上传图片文件")
+    @PostMapping(value = "/image", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String uploadImageFile(@NotNull MultipartFile file, String userId) throws Exception {
+        String filename = file.getOriginalFilename();
+        long size = file.getSize();
+        String contentType = file.getContentType();
+        InputStream inputStream = file.getInputStream();
+        if (contentType == null || !contentType.startsWith("image")) {
+            return WebBody.failWithMsg("content-type 错误");
+        }
 
 
-    @ApiOperation("添加视频文件")
-    @PostMapping("/video")
-    public String addVideoFile(@RequestBody @Validated SpiderVideoFile spiderVideoFile) {
-        spiderFileService.addVideoFile(spiderVideoFile);
-        return WebBody.success();
+        UploadedFile uploadedFile = new UploadedFile(filename, size, contentType, inputStream);
+        UploadFileRet uploadFileRet = fileUploadService.put(uploadedFile);
+        return WebBody.success(uploadFileRet);
     }
     }
 
 
-    @ApiOperation("添加图片文件")
-    @PostMapping("/images")
-    public String addImageFiles(@RequestBody @Validated SpiderImageFile spiderImageFile) {
-        spiderFileService.addImageFile(spiderImageFile);
+    @ApiOperation(value = "上传视频文件")
+    @PostMapping(value = "/video", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String uploadVideoFile(@Validated UploadFile uploadFile) throws Exception {
+        log.info("视频文件");
         return WebBody.success();
         return WebBody.success();
     }
     }
 }
 }

+ 31 - 2
src/main/java/cn/reghao/tnb/file/app/db/repository/FileRepository.java

@@ -71,6 +71,22 @@ public class FileRepository {
         return new FileMap(fileId, uploadId);
         return new FileMap(fileId, uploadId);
     }
     }
 
 
+    @Deprecated
+    public FileMap createUploadId(UploadedFile uploadedFile, String sha256sum, int size, String currentUserId) {
+        String filename = uploadedFile.getFilename();
+        String suffix = StringUtil.getSuffix(filename);
+        String contentType = uploadedFile.getContentType();
+        String fileId = idGenerator.getUuid();
+        FileInfo fileInfo = new FileInfo(fileId, sha256sum, filename, size, suffix, contentType);
+
+        String uploadId = idGenerator.getUuid();
+        FileUser fileUser = new FileUser(uploadId, fileId, currentUserId);
+
+        fileInfoMapper.save(fileInfo);
+        fileUserMapper.save(fileUser);
+        return new FileMap(fileId, uploadId);
+    }
+
     public String getOrCreateUploadId(String fileId) {
     public String getOrCreateUploadId(String fileId) {
         String currentUserId = UserContext.getUserId();
         String currentUserId = UserContext.getUserId();
         FileUser fileUser = fileUserMapper.findByFileAndUserId(fileId, currentUserId);
         FileUser fileUser = fileUserMapper.findByFileAndUserId(fileId, currentUserId);
@@ -84,6 +100,19 @@ public class FileRepository {
         }
         }
     }
     }
 
 
+    @Deprecated
+    public String getOrCreateUploadId(String fileId, String currentUserId) {
+        FileUser fileUser = fileUserMapper.findByFileAndUserId(fileId, currentUserId);
+        if (fileUser != null) {
+            return fileUser.getUploadId();
+        } else {
+            String uploadId = idGenerator.getUuid();
+            fileUser = new FileUser(uploadId, fileId, currentUserId);
+            fileUserMapper.save(fileUser);
+            return uploadId;
+        }
+    }
+
     public FileInfo getFileInfo(String sha256sum) {
     public FileInfo getFileInfo(String sha256sum) {
         return fileInfoMapper.findFileInfoBySha256sum(sha256sum);
         return fileInfoMapper.findFileInfoBySha256sum(sha256sum);
     }
     }
@@ -118,8 +147,8 @@ public class FileRepository {
     public void saveFile(String fileId, FileContentType fileContentType, PathUrl pathUrl) throws Exception {
     public void saveFile(String fileId, FileContentType fileContentType, PathUrl pathUrl) throws Exception {
         int fileType = fileContentType.getFileType();
         int fileType = fileContentType.getFileType();
         String contentType = fileContentType.getContentType();
         String contentType = fileContentType.getContentType();
-        String blockId = LocalStores.getBlockId1(pathUrl.getFilePath());
-        String relativePath = LocalStores.getRelativePath(pathUrl.getFilePath());
+        String blockId = LocalStores.getBlockId(pathUrl.getAbsolutePath());
+        String relativePath = LocalStores.getRelativePath(pathUrl.getAbsolutePath());
         FileUrl fileUrl = new FileUrl(fileId, blockId, relativePath, pathUrl.getUrl(), pathUrl.getPath());
         FileUrl fileUrl = new FileUrl(fileId, blockId, relativePath, pathUrl.getUrl(), pathUrl.getPath());
 
 
         // TODO 文件上传完成后, 立即同步到其它节点
         // TODO 文件上传完成后, 立即同步到其它节点

+ 1 - 1
src/main/java/cn/reghao/tnb/file/app/model/dto/PathUrl.java

@@ -12,7 +12,7 @@ import lombok.Getter;
 public class PathUrl {
 public class PathUrl {
     private String blockId;
     private String blockId;
     // 本地文件路径
     // 本地文件路径
-    private String filePath;
+    private String absolutePath;
     private String url;
     private String url;
     // 不包括域名的路径
     // 不包括域名的路径
     private String path;
     private String path;

+ 2 - 0
src/main/java/cn/reghao/tnb/file/app/model/vo/UploadFileRet.java

@@ -1,6 +1,7 @@
 package cn.reghao.tnb.file.app.model.vo;
 package cn.reghao.tnb.file.app.model.vo;
 
 
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
+import lombok.Getter;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 
 
@@ -9,6 +10,7 @@ import java.io.Serializable;
  * @date 2022-04-26 15:22:49
  * @date 2022-04-26 15:22:49
  */
  */
 @AllArgsConstructor
 @AllArgsConstructor
+@Getter
 public class UploadFileRet implements Serializable {
 public class UploadFileRet implements Serializable {
     private static final long serialVersionUID = 1L;
     private static final long serialVersionUID = 1L;
 
 

+ 13 - 7
src/main/java/cn/reghao/tnb/file/app/service/FileStoreService.java

@@ -4,6 +4,11 @@ import org.apache.commons.io.FileUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.io.*;
 import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.EnumSet;
+
+import static java.nio.file.StandardOpenOption.*;
 
 
 /**
 /**
  * @author reghao
  * @author reghao
@@ -11,11 +16,12 @@ import java.io.*;
  */
  */
 @Service
 @Service
 public class FileStoreService {
 public class FileStoreService {
-    public File createFile(String filePath, long len) throws IOException {
-        File file = new File(filePath);
+    public File createFile(String absolutePath, long len) throws IOException {
+        File file = new File(absolutePath);
         if (!file.exists()) {
         if (!file.exists()) {
             FileUtils.forceMkdirParent(file);
             FileUtils.forceMkdirParent(file);
-            file.createNewFile();
+            // 创建一个稀疏文件
+            Files.newByteChannel(Paths.get(absolutePath), EnumSet.of(CREATE_NEW, WRITE, SPARSE));
             RandomAccessFile raf = new RandomAccessFile(file, "rw");
             RandomAccessFile raf = new RandomAccessFile(file, "rw");
             raf.setLength(len);
             raf.setLength(len);
             raf.close();
             raf.close();
@@ -35,8 +41,8 @@ public class FileStoreService {
         in.close();
         in.close();
     }
     }
 
 
-    public void saveFile(String filePath, InputStream in) throws IOException {
-        File file = new File(filePath);
+    public void saveFile(String absolutePath, InputStream in) throws IOException {
+        File file = new File(absolutePath);
         if (file.exists()) {
         if (file.exists()) {
             return;
             return;
         }
         }
@@ -57,8 +63,8 @@ public class FileStoreService {
         fos.close();
         fos.close();
     }
     }
 
 
-    public void saveFile(String filePath, byte[] bytes) throws IOException {
-        File file = new File(filePath);
+    public void saveFile(String absolutePath, byte[] bytes) throws IOException {
+        File file = new File(absolutePath);
         if (file.exists()) {
         if (file.exists()) {
             return;
             return;
         }
         }

+ 58 - 7
src/main/java/cn/reghao/tnb/file/app/service/FileUploadService.java

@@ -11,6 +11,7 @@ import cn.reghao.tnb.file.app.model.dto.*;
 import cn.reghao.tnb.file.app.model.po.FileInfo;
 import cn.reghao.tnb.file.app.model.po.FileInfo;
 import cn.reghao.tnb.file.app.util.StringUtil;
 import cn.reghao.tnb.file.app.util.StringUtil;
 import cn.reghao.tnb.file.app.util.media.ImageOps;
 import cn.reghao.tnb.file.app.util.media.ImageOps;
+import cn.reghao.tnb.file.app.util.store.LocalStores;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.annotation.Transactional;
@@ -99,8 +100,15 @@ public class FileUploadService {
                     size1.getWidth(), size1.getHeight());
                     size1.getWidth(), size1.getHeight());
         }*/
         }*/
 
 
-        FileContentType fileType = fileTypeService.getFileType(contentType, pathUrl.getFilePath());
-        fileStoreService.saveFile(pathUrl.getFilePath(), bytes);
+        FileContentType fileType = fileTypeService.getFileType(contentType, pathUrl.getAbsolutePath());
+        try {
+            fileStoreService.saveFile(pathUrl.getAbsolutePath(), bytes);
+            LocalStores.getStoreDir(pathUrl.getAbsolutePath()).incrCount();
+        } catch (Exception e) {
+            LocalStores.getStoreDir(pathUrl.getAbsolutePath()).decrCount();
+            throw e;
+        }
+
         fileRepository.saveFile(fileId, fileType, pathUrl);
         fileRepository.saveFile(fileId, fileType, pathUrl);
         return pathUrl;
         return pathUrl;
     }
     }
@@ -125,10 +133,53 @@ public class FileUploadService {
             String fileId = fileMap.getFileId();
             String fileId = fileMap.getFileId();
             String uploadId = fileMap.getUploadId();
             String uploadId = fileMap.getUploadId();
             PathUrl pathUrl = fileUrlService.genPathUrl(contentType, sha256sum, size, fileId, suffix, uploadId);
             PathUrl pathUrl = fileUrlService.genPathUrl(contentType, sha256sum, size, fileId, suffix, uploadId);
-            FileContentType fileType = fileTypeService.getFileType(contentType, pathUrl.getFilePath());
-            fileStoreService.saveFile(pathUrl.getFilePath(), bytes);
+            FileContentType fileType = fileTypeService.getFileType(contentType, pathUrl.getAbsolutePath());
+            try {
+                fileStoreService.saveFile(pathUrl.getAbsolutePath(), bytes);
+                LocalStores.getStoreDir(pathUrl.getAbsolutePath()).incrCount();
+            } catch (Exception e) {
+                LocalStores.getStoreDir(pathUrl.getAbsolutePath()).decrCount();
+                throw e;
+            }
+
             fileRepository.saveFile(fileId, fileType, pathUrl);
             fileRepository.saveFile(fileId, fileType, pathUrl);
+            String url = pathUrl.getUrl();
+            return new UploadFileRet(uploadId, url);
+        }
+    }
+
+    @Deprecated
+    @Transactional(rollbackFor = Exception.class)
+    public synchronized UploadFileRet put(UploadedFile uploadedFile, String currentUserId) throws Exception {
+        byte[] bytes = uploadedFile.getInputStream().readAllBytes();
+        String sha256sum = DigestUtil.sha256sum(bytes);
+        FileInfo fileInfo = fileRepository.getFileInfo(sha256sum);
+        if (fileInfo != null) {
+            String fileId = fileInfo.getFileId();
+            String uploadId = fileRepository.getOrCreateUploadId(fileInfo.getFileId(), currentUserId);
+            String url = fileRepository.getPathUrlByFileId(fileId);
+            return new UploadFileRet(uploadId, url);
+        } else {
+            int size = bytes.length;
+            String filename = uploadedFile.getFilename();
+            String suffix = StringUtil.getSuffix(filename);
+            String contentType = uploadedFile.getContentType();
+            FileMap fileMap = fileRepository.createUploadId(uploadedFile, sha256sum, size, currentUserId);
 
 
+            String fileId = fileMap.getFileId();
+            String uploadId = fileMap.getUploadId();
+            PathUrl pathUrl = fileUrlService.genPathUrl(contentType, sha256sum, size, fileId, suffix, uploadId);
+            String absolutePath = pathUrl.getAbsolutePath();
+            FileContentType fileType = fileTypeService.getFileType(contentType, absolutePath);
+            try {
+                fileStoreService.saveFile(absolutePath, bytes);
+                LocalStores.getStoreDir(absolutePath).incrCount();
+            } catch (Exception e) {
+                LocalStores.getStoreDir(absolutePath).decrCount();
+                throw e;
+            }
+
+            fileRepository.saveFile(fileId, fileType, pathUrl);
             String url = pathUrl.getUrl();
             String url = pathUrl.getUrl();
             return new UploadFileRet(uploadId, url);
             return new UploadFileRet(uploadId, url);
         }
         }
@@ -162,7 +213,7 @@ public class FileUploadService {
         if (!set.contains(partIndex)) {
         if (!set.contains(partIndex)) {
             set.add(partIndex);
             set.add(partIndex);
             long size = fileInfo.getSize();
             long size = fileInfo.getSize();
-            File file = fileStoreService.createFile(pathUrl.getFilePath(), size);
+            File file = fileStoreService.createFile(pathUrl.getAbsolutePath(), size);
             long pos = partIndex * PART_SIZE;
             long pos = partIndex * PART_SIZE;
             fileStoreService.writeToFile(multipartFile.getInputStream(), file, pos);
             fileStoreService.writeToFile(multipartFile.getInputStream(), file, pos);
         }
         }
@@ -171,14 +222,14 @@ public class FileUploadService {
         if (len != partNum) {
         if (len != partNum) {
             return new UploadFilePartRet(fileId, false);
             return new UploadFilePartRet(fileId, false);
         } else {
         } else {
-            String path = pathUrl.getFilePath();
+            String path = pathUrl.getAbsolutePath();
             FileInputStream fis = new FileInputStream(path);
             FileInputStream fis = new FileInputStream(path);
             String sha256sumMerged = DigestUtil.sha256sum(fis);
             String sha256sumMerged = DigestUtil.sha256sum(fis);
             if (!sha256sum.equals(sha256sumMerged)) {
             if (!sha256sum.equals(sha256sumMerged)) {
                 throw new Exception("uploadId 和 sha256sum 不匹配!");
                 throw new Exception("uploadId 和 sha256sum 不匹配!");
             }
             }
 
 
-            FileContentType fileType = fileTypeService.getFileType(fileInfo.getContentType(), pathUrl.getFilePath());
+            FileContentType fileType = fileTypeService.getFileType(fileInfo.getContentType(), pathUrl.getAbsolutePath());
             fileRepository.saveFile(fileId, fileType, pathUrl);
             fileRepository.saveFile(fileId, fileType, pathUrl);
             map.remove(fileId);
             map.remove(fileId);
             return new UploadFilePartRet(uploadId, true);
             return new UploadFilePartRet(uploadId, true);

+ 6 - 4
src/main/java/cn/reghao/tnb/file/app/service/FileUrlService.java

@@ -46,9 +46,10 @@ public class FileUrlService {
 
 
     public PathUrl genPathAndUrl(String sha256sum, long fileSize, String fileId, String suffix, String path)
     public PathUrl genPathAndUrl(String sha256sum, long fileSize, String fileId, String suffix, String path)
             throws IOException, NoSuchAlgorithmException {
             throws IOException, NoSuchAlgorithmException {
-        StoreDir storeDir = loadBalancer.getStoreDir(fileSize, sha256sum);
+        //StoreDir storeDir = loadBalancer.getStoreDir(fileSize, sha256sum);
+        StoreDir storeDir = loadBalancer.getStoreDir(fileSize);
         String blockId = storeDir.getBlockId();
         String blockId = storeDir.getBlockId();
-        String fileDir = storeDir.getFileDir();
+        String fileDir = storeDir.getAbsoluteDirPath();
 
 
         String filePath = String.format("%s/%s.%s", fileDir, fileId, suffix);
         String filePath = String.format("%s/%s.%s", fileDir, fileId, suffix);
         String url = String.format("//%s/%s", domain, path);
         String url = String.format("//%s/%s", domain, path);
@@ -58,9 +59,10 @@ public class FileUrlService {
     @Deprecated
     @Deprecated
     public PathUrl getImagePathAndUrl(String sha256sum, String uploadId, long fileSize, String fileId, String suffix,
     public PathUrl getImagePathAndUrl(String sha256sum, String uploadId, long fileSize, String fileId, String suffix,
                                       int width, int height) throws IOException, NoSuchAlgorithmException {
                                       int width, int height) throws IOException, NoSuchAlgorithmException {
-        StoreDir storeDir = loadBalancer.getStoreDir(fileSize, sha256sum);
+        //StoreDir storeDir = loadBalancer.getStoreDir(fileSize, sha256sum);
+        StoreDir storeDir = loadBalancer.getStoreDir(fileSize);
         String blockId = storeDir.getBlockId();
         String blockId = storeDir.getBlockId();
-        String fileDir = storeDir.getFileDir();
+        String fileDir = storeDir.getAbsoluteDirPath();
 
 
         String filePath = String.format("%s/%s_%sx%s.%s", fileDir, fileId, width, height, suffix);
         String filePath = String.format("%s/%s_%sx%s.%s", fileDir, fileId, width, height, suffix);
         String path = String.format("video/%s.%s", uploadId, suffix);
         String path = String.format("video/%s.%s", uploadId, suffix);

+ 2 - 71
src/main/java/cn/reghao/tnb/file/app/service/SpiderFileService.java

@@ -8,8 +8,6 @@ import cn.reghao.tnb.file.app.model.po.*;
 import cn.reghao.tnb.file.app.util.store.LocalStores;
 import cn.reghao.tnb.file.app.util.store.LocalStores;
 import cn.reghao.tnb.file.app.util.StringUtil;
 import cn.reghao.tnb.file.app.util.StringUtil;
 import cn.reghao.tnb.file.client.model.SpiderFile;
 import cn.reghao.tnb.file.client.model.SpiderFile;
-import cn.reghao.tnb.file.client.model.SpiderImageFile;
-import cn.reghao.tnb.file.client.model.SpiderVideoFile;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.annotation.Transactional;
 
 
@@ -41,74 +39,7 @@ public class SpiderFileService {
     }
     }
 
 
     @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = Exception.class)
-    public String addFile(SpiderFile spiderFile) {
-        String fileId = spiderFile.getFileId();
-        String userId = spiderFile.getUserId();
-        String url = spiderFile.getUrl();
-        FileInfo fileInfo = fileInfoMapper.findByFileId(fileId);
-        if (fileInfo != null) {
-            return fileUrlMapper.findByFileId(fileId).get(0).getUrl();
-        }
-
-        String sha256sum = fileId;
-        String filename = UrlFormatter.getFilename(url);
-        String suffix = StringUtil.getSuffix(filename);
-        long size = 0;
-        String contentType = "";
-        fileInfo = new FileInfo(fileId, sha256sum, filename, size, suffix, contentType);
-
-        String uploadId = idGenerator.getUuid();
-        FileUser fileUser = new FileUser(uploadId, fileId, userId);
-
-        String filePath = url.replace("//file1.reghao.cn", "");
-        String blockId = LocalStores.getBlockId(filePath);
-        String path = "";
-        switch (suffix) {
-            case "jpg":
-                path = String.format("/image/%s.%s", uploadId, suffix);
-                break;
-            case "mp4":
-                path = String.format("/video/playback/%s.%s", uploadId, suffix);
-                break;
-            case "m3u8":
-                path = String.format("/video/hls/%s.%s", uploadId, suffix);
-                break;
-            case "mpd":
-                path = String.format("/video/dash/%s/index.%s", uploadId, suffix);
-                break;
-            default:
-        }
-
-        String url1 = String.format("//%s%s", domain, path);
-        FileUrl fileUrl = new FileUrl(fileId, blockId, filePath, url1, path);
-
-        fileInfoMapper.save(fileInfo);
-        fileUrlMapper.save(fileUrl);
-        fileUserMapper.save(fileUser);
-        return url1;
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    public void addVideoFile(SpiderVideoFile spiderVideoFile) {
-        String userId = spiderVideoFile.getUserId();
-        String videoFileId = spiderVideoFile.getVideoId();
-        String videoUrl = spiderVideoFile.getVideoUrl();
-        SpiderFile spiderFile = new SpiderFile(videoFileId, userId, videoUrl);
-        String videoUrl1 = addFile(spiderFile);
-
-        String coverFileId = String.format("cover%s", videoFileId);
-        String coverUrl = spiderVideoFile.getCoverUrl();
-        SpiderFile spiderFile1 = new SpiderFile(coverFileId, userId, coverUrl);
-        String coverUrl1 = addFile(spiderFile1);
-
-        VideoFile videoFile = new VideoFile(spiderVideoFile);
-        videoFile.setBaseUrl(videoUrl1);
-        videoFile.setBaseCoverUrl(coverUrl1);
-        videoFileMapper.save(videoFile);
-    }
-
-    public void addImageFile(SpiderImageFile spiderImageFile) {
-        ImageFile imageFile = new ImageFile(spiderImageFile);
-        imageFileMapper.save(imageFile);
+    public String addFile(SpiderFile spiderFile) throws Exception {
+        return null;
     }
     }
 }
 }

+ 1 - 1
src/main/java/cn/reghao/tnb/file/app/service/media/ImageFileService.java

@@ -27,7 +27,7 @@ public class ImageFileService {
     }
     }
 
 
     public ImgFileRetDto process(String fileId, PathUrl pathUrl) throws Exception {
     public ImgFileRetDto process(String fileId, PathUrl pathUrl) throws Exception {
-        String path = pathUrl.getFilePath();
+        String path = pathUrl.getAbsolutePath();
         String url = pathUrl.getUrl();
         String url = pathUrl.getUrl();
         File imgLocalFile = new File(path);
         File imgLocalFile = new File(path);
 
 

+ 1 - 1
src/main/java/cn/reghao/tnb/file/app/service/media/VideoFileService.java

@@ -42,7 +42,7 @@ public class VideoFileService {
      */
      */
     @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = Exception.class)
     public VideoFile process(String fileId, PathUrl pathUrl) throws Exception {
     public VideoFile process(String fileId, PathUrl pathUrl) throws Exception {
-        String path = pathUrl.getFilePath();
+        String path = pathUrl.getAbsolutePath();
         String url = pathUrl.getUrl();
         String url = pathUrl.getUrl();
 
 
         VideoFile videoFile = new VideoFile(fileId, VideoUrlType.mp4.getName(), url);
         VideoFile videoFile = new VideoFile(fileId, VideoUrlType.mp4.getName(), url);

+ 16 - 4
src/main/java/cn/reghao/tnb/file/app/util/store/LoadBalancer.java

@@ -19,14 +19,26 @@ import java.util.*;
 public class LoadBalancer {
 public class LoadBalancer {
     private final HashFunction hashFunction = Hashing.murmur3_32_fixed();
     private final HashFunction hashFunction = Hashing.murmur3_32_fixed();
 
 
+    public StoreDir getStoreDir(long fileSize) {
+        LocalStore localStore = LocalStores.getMaxStore(fileSize);
+        List<StoreDir> subDirs = LocalStores.getSubDirs(localStore.getBlockId());
+        Collections.sort(subDirs, new Comparator<StoreDir>() {
+            @Override
+            public int compare(StoreDir storeDir1, StoreDir storeDir2) {
+                return storeDir1.getCount() - storeDir2.getCount();
+            }
+        });
+
+        return subDirs.get(0);
+    }
+
     public StoreDir getStoreDir(long fileSize, String sha256sum) throws IOException, NoSuchAlgorithmException {
     public StoreDir getStoreDir(long fileSize, String sha256sum) throws IOException, NoSuchAlgorithmException {
         LocalStore localStore = LocalStores.getMaxStore(fileSize);
         LocalStore localStore = LocalStores.getMaxStore(fileSize);
-        String blockId = localStore.getBlockId();
-        List<String> subDirs = LocalStores.getSubDirs(localStore.getBaseDir());
+        List<StoreDir> subDirs = LocalStores.getSubDirs(localStore.getBlockId());
+
         int hash = hash(sha256sum);
         int hash = hash(sha256sum);
         int nodeIdx = hash % subDirs.size();
         int nodeIdx = hash % subDirs.size();
-        String dir = subDirs.get(nodeIdx);
-        return new StoreDir(blockId, dir);
+        return null;
     }
     }
 
 
     private int hash(Object object) throws IOException, NoSuchAlgorithmException {
     private int hash(Object object) throws IOException, NoSuchAlgorithmException {

+ 9 - 8
src/main/java/cn/reghao/tnb/file/app/util/store/LocalStore.java

@@ -6,6 +6,7 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.math.RoundingMode;
 import java.nio.file.FileStore;
 import java.nio.file.FileStore;
+import java.util.concurrent.atomic.AtomicLong;
 
 
 /**
 /**
  * @author reghao
  * @author reghao
@@ -20,7 +21,7 @@ public class LocalStore {
     private final String fsType;
     private final String fsType;
     private final String mountedOn;
     private final String mountedOn;
     private final long total;
     private final long total;
-    private volatile long available;
+    private final AtomicLong available;
     private final long max;
     private final long max;
 
 
     public LocalStore(String baseDir, FileStore fileStore, String blockId, double maxPercent) throws IOException {
     public LocalStore(String baseDir, FileStore fileStore, String blockId, double maxPercent) throws IOException {
@@ -31,7 +32,7 @@ public class LocalStore {
         this.fsType = fileStore.type();
         this.fsType = fileStore.type();
         this.mountedOn = fileStore.toString().replace(fs, "").replace(" ()", "");
         this.mountedOn = fileStore.toString().replace(fs, "").replace(" ()", "");
         this.total = fileStore.getTotalSpace();
         this.total = fileStore.getTotalSpace();
-        this.available = fileStore.getUsableSpace();
+        this.available = new AtomicLong(fileStore.getUsableSpace());
 
 
         BigDecimal bigDecimal1 = new BigDecimal(total*10);
         BigDecimal bigDecimal1 = new BigDecimal(total*10);
         BigDecimal bigDecimal2 = new BigDecimal(maxPercent*10);
         BigDecimal bigDecimal2 = new BigDecimal(maxPercent*10);
@@ -39,16 +40,16 @@ public class LocalStore {
         this.max = result.longValue();
         this.max = result.longValue();
     }
     }
 
 
-    public void setAvailable(long available) {
-        this.available = available;
+    public void incrAvail(long avail) {
+        available.addAndGet(avail);
     }
     }
 
 
-    public boolean available(long len) throws IOException {
-        return total - fileStore.getUsableSpace() + len > max;
+    public void setAvail(long avail) {
+        available.getAndSet(avail);
     }
     }
 
 
-    public long availableSize(long len) throws IOException {
-        return total - fileStore.getUsableSpace() + len;
+    public long getAvail() {
+        return available.get();
     }
     }
 
 
     @Override
     @Override

+ 49 - 65
src/main/java/cn/reghao/tnb/file/app/util/store/LocalStores.java

@@ -10,20 +10,17 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.Paths;
 import java.util.*;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 /**
 /**
  * @author reghao
  * @author reghao
  * @date 2022-05-23 18:21:22
  * @date 2022-05-23 18:21:22
  */
  */
 public class LocalStores {
 public class LocalStores {
-    private static String domain;
-    @Deprecated
     private static final Map<String, LocalStore> storeMap = new HashMap<>();
     private static final Map<String, LocalStore> storeMap = new HashMap<>();
-    private static final Map<String, LocalStore> storeMap1 = new HashMap<>();
-    private static final Map<String, List<String>> subDirs = new HashMap<>();
+    private static final Map<String, Map<String, StoreDir>> subDirs = new HashMap<>();
 
 
     public static void init(DfsProperties dfsProperties) throws IOException {
     public static void init(DfsProperties dfsProperties) throws IOException {
-        domain = dfsProperties.getDomain();
         Map<String, String> blockIdMap = getBlockIdMap();
         Map<String, String> blockIdMap = getBlockIdMap();
         Map<String, String> checker = new HashMap<>();
         Map<String, String> checker = new HashMap<>();
         for (String baseDir : dfsProperties.getBaseDirs()) {
         for (String baseDir : dfsProperties.getBaseDirs()) {
@@ -41,57 +38,12 @@ public class LocalStores {
             }
             }
 
 
             LocalStore localStore = new LocalStore(baseDir, fileStore, blockId, 0.9);
             LocalStore localStore = new LocalStore(baseDir, fileStore, blockId, 0.9);
-            storeMap.put(baseDir, localStore);
-            storeMap1.put(blockId, localStore);
-            subDirs.computeIfAbsent(baseDir, k -> new ArrayList<>());
-            createSubDirs(baseDir);
+            storeMap.put(blockId, localStore);
+            subDirs.computeIfAbsent(blockId, k -> new HashMap<>());
+            createSubDirs(blockId, baseDir);
         }
         }
     }
     }
 
 
-    public static String getDomain() {
-        return domain;
-    }
-
-    // TODO 临时在 VideoFileServiceImpl 中使用
-    @Deprecated
-    public static String getBlockId(String path) {
-        List<String> blockIds = new ArrayList<>();
-        for (Map.Entry<String, LocalStore> entry : storeMap1.entrySet()) {
-            String blockId = entry.getKey();
-            LocalStore localStore = entry.getValue();
-            String baseDir = localStore.getBaseDir();
-            File file = new File(baseDir + path);
-            if (file.exists()) {
-                blockIds.add(blockId);
-            }
-        }
-
-        return blockIds.get(0);
-    }
-
-    public static String getBlockId1(String filePath) throws Exception {
-        for (Map.Entry<String, LocalStore> entry : storeMap1.entrySet()) {
-            String blockId = entry.getKey();
-            LocalStore localStore = entry.getValue();
-            String baseDir = localStore.getBaseDir();
-            if (filePath.startsWith(baseDir)) {
-                return blockId;
-            }
-        }
-        throw new Exception(String.format("没有找到 %s 对应的 blockId", filePath));
-    }
-
-    public static String getRelativePath(String filePath) throws Exception {
-        for (Map.Entry<String, LocalStore> entry : storeMap1.entrySet()) {
-            LocalStore localStore = entry.getValue();
-            String baseDir = localStore.getBaseDir();
-            if (filePath.startsWith(baseDir)) {
-                return filePath.replace(baseDir, "");
-            }
-        }
-        throw new Exception(String.format("没有找到 %s 对应的 block", filePath));
-    }
-
     private static Map<String, String> getBlockIdMap() throws IOException {
     private static Map<String, String> getBlockIdMap() throws IOException {
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
         File dir = new File("/dev/disk/by-uuid");
         File dir = new File("/dev/disk/by-uuid");
@@ -104,36 +56,63 @@ public class LocalStores {
         return map;
         return map;
     }
     }
 
 
-    private static void createSubDirs(String baseDir) throws IOException {
+    private static void createSubDirs(String blockId, String baseDir) throws IOException {
         int total = 128;
         int total = 128;
         for (int i = 0; i < total; i++) {
         for (int i = 0; i < total; i++) {
             for (int j = 0; j < total; j++) {
             for (int j = 0; j < total; j++) {
-                String dirPath = String.format("%s%s/%s/", baseDir, i, j);
-                File dir = new File(dirPath);
+                String absoluteDirPath = String.format("%s%s/%s/", baseDir, i, j);
+                File dir = new File(absoluteDirPath);
+                int filesCount = 0;
                 if (!dir.exists()) {
                 if (!dir.exists()) {
                     FileUtils.forceMkdir(dir);
                     FileUtils.forceMkdir(dir);
+                } else {
+                    filesCount = Objects.requireNonNull(dir.list()).length;
                 }
                 }
-                subDirs.get(baseDir).add(dirPath);
+                StoreDir storeDir = new StoreDir(blockId, new AtomicInteger(filesCount), absoluteDirPath);
+                subDirs.get(blockId).put(absoluteDirPath, storeDir);
             }
             }
         }
         }
     }
     }
 
 
+    public static String getBlockId(String absolutePath) throws Exception {
+        for (Map.Entry<String, LocalStore> entry : storeMap.entrySet()) {
+            String blockId = entry.getKey();
+            LocalStore localStore = entry.getValue();
+            String baseDir = localStore.getBaseDir();
+            if (absolutePath.startsWith(baseDir)) {
+                return blockId;
+            }
+        }
+        throw new Exception(String.format("没有找到 %s 对应的 blockId", absolutePath));
+    }
+
+    public static String getRelativePath(String absolutePath) throws Exception {
+        for (Map.Entry<String, LocalStore> entry : storeMap.entrySet()) {
+            LocalStore localStore = entry.getValue();
+            String baseDir = localStore.getBaseDir();
+            if (absolutePath.startsWith(baseDir)) {
+                return absolutePath.replace(baseDir, "");
+            }
+        }
+        throw new Exception(String.format("没有找到 %s 对应的 block", absolutePath));
+    }
+
     public static String getMountedOn(String blockId) {
     public static String getMountedOn(String blockId) {
-        return storeMap1.get(blockId).getMountedOn();
+        return storeMap.get(blockId).getMountedOn();
     }
     }
 
 
     public static String getBaseDir(String blockId) {
     public static String getBaseDir(String blockId) {
-        return storeMap1.get(blockId).getBaseDir();
+        return storeMap.get(blockId).getBaseDir();
     }
     }
 
 
     // TODO 优化算法, 处理异常
     // TODO 优化算法, 处理异常
     public static LocalStore getMaxStore(long size) {
     public static LocalStore getMaxStore(long size) {
         Map<String, Long> map = new HashMap<>();
         Map<String, Long> map = new HashMap<>();
         for (Map.Entry<String, LocalStore> entry : storeMap.entrySet()) {
         for (Map.Entry<String, LocalStore> entry : storeMap.entrySet()) {
-            String baseDir = entry.getKey();
+            String blockId = entry.getKey();
             LocalStore localStore = entry.getValue();
             LocalStore localStore = entry.getValue();
-            long availSize = localStore.getAvailable() - size;
-            map.put(baseDir, availSize);
+            long availSize = localStore.getAvail() - size;
+            map.put(blockId, availSize);
         }
         }
 
 
         List<String> baseDirs = new ArrayList<>();
         List<String> baseDirs = new ArrayList<>();
@@ -144,11 +123,16 @@ public class LocalStores {
 
 
         String baseDir = baseDirs.get(baseDirs.size()-1);
         String baseDir = baseDirs.get(baseDirs.size()-1);
         LocalStore localStore = storeMap.get(baseDir);
         LocalStore localStore = storeMap.get(baseDir);
-        localStore.setAvailable(map.get(baseDir));
+        localStore.setAvail(map.get(baseDir));
         return localStore;
         return localStore;
     }
     }
+    
+    public static List<StoreDir> getSubDirs(String blockId) {
+        return new ArrayList<>(subDirs.get(blockId).values());
+    }
 
 
-    public static List<String> getSubDirs(String store) {
-        return subDirs.get(store);
+    public static StoreDir getStoreDir(String absolutePath) throws Exception {
+        String blockId = getBlockId(absolutePath);
+        return subDirs.get(blockId).get(new File(absolutePath).getParent() + File.separator);
     }
     }
 }
 }

+ 24 - 3
src/main/java/cn/reghao/tnb/file/app/util/store/StoreDir.java

@@ -1,15 +1,36 @@
 package cn.reghao.tnb.file.app.util.store;
 package cn.reghao.tnb.file.app.util.store;
 
 
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
-import lombok.Getter;
+
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 /**
 /**
  * @author reghao
  * @author reghao
  * @date 2022-05-23 23:31:21
  * @date 2022-05-23 23:31:21
  */
  */
 @AllArgsConstructor
 @AllArgsConstructor
-@Getter
 public class StoreDir {
 public class StoreDir {
     private String blockId;
     private String blockId;
-    private String fileDir;
+    private volatile AtomicInteger filesCount;
+    private String absoluteDirPath;
+
+    public String getBlockId() {
+        return blockId;
+    }
+
+    public int getCount() {
+        return filesCount.get();
+    }
+
+    public void incrCount() {
+        filesCount.incrementAndGet();
+    }
+
+    public void decrCount() {
+        filesCount.decrementAndGet();
+    }
+
+    public String getAbsoluteDirPath() {
+        return absoluteDirPath;
+    }
 }
 }

+ 2 - 2
src/main/resources/application-dev.yml

@@ -6,5 +6,5 @@ spring:
 dfs:
 dfs:
   domain: file.reghao.cn
   domain: file.reghao.cn
   baseDirs:
   baseDirs:
-    - /opt/file/disk/
-    - /home/reghao/opt/file/disk/
+    - /opt/file/disk0/
+    - /home/reghao/opt/file/disk0/

+ 17 - 0
src/test/java/FileTest.java

@@ -0,0 +1,17 @@
+import org.hashids.Hashids;
+import org.junit.Test;
+
+/**
+ * @author reghao
+ * @date 2022-06-20 14:31:14
+ */
+public class FileTest {
+    Hashids hashids = new Hashids("file-id", 8);
+
+    @Test
+    public void test() {
+        String fileIdStr = "NJg4lx72RB";
+        long[] fileId = hashids.decode(fileIdStr);
+        System.out.println();
+    }
+}

+ 0 - 56
src/test/java/FileTest1.java

@@ -1,56 +0,0 @@
-import org.junit.Test;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.file.*;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.EnumSet;
-
-import static java.nio.file.StandardOpenOption.*;
-
-/**
- * @author reghao
- * @date 2022-04-23 17:17:50
- */
-public class FileTest1 {
-    @Test
-    public void writeTest() throws IOException {
-        FileInputStream fis = new FileInputStream("/home/reghao/Downloads/pic/818241ae.jpeg");
-        String filePath = "/home/reghao/Downloads/dfs/test.dat";
-        RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
-        raf.seek(0);
-        raf.write(fis.readAllBytes());
-        raf.close();
-    }
-
-    @Test
-    public void createTest() throws IOException {
-        String filePath = "/home/reghao/Downloads/dfs/test2.dat";
-        createSparseFile(filePath);
-    }
-
-    void createFile(String filePath) throws IOException {
-        File file = new File(filePath);
-        if (file.exists()) {
-            throw new IOException(String.format("%s 已存在", filePath));
-        }
-
-        boolean created = file.createNewFile();
-        if (!created) {
-            throw new IOException(String.format("创建 %s 失败", filePath));
-        }
-
-        RandomAccessFile raf = new RandomAccessFile(file, "rw");
-        raf.setLength(1024*1024*128);
-        raf.close();
-    }
-
-    void createSparseFile(String filePath) throws IOException {
-        Files.newByteChannel(Paths.get(filePath), EnumSet.of(CREATE_NEW, WRITE, SPARSE));
-        RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
-        raf.setLength(1024*1024*128);
-        raf.close();
-    }
-}