reghao 3 tahun lalu
induk
melakukan
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;
 
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 
@@ -9,10 +11,13 @@ import java.util.List;
  * @author reghao
  * @date 2021-12-30 11:01:46
  */
+@AllArgsConstructor
+@NoArgsConstructor
 @Component
 @ConfigurationProperties(prefix = "dfs")
 public class DfsProperties {
     private String domain;
+    // TODO 修改为 mountedDirs, 表示磁盘挂载的目录
     private List<String> baseDirs;
 
     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.tnb.file.app.db.repository.FileRepository;
-import cn.reghao.tnb.file.app.service.SpiderFileService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
 /**
  * @author reghao
- * @date 2020-03-11 16:10:16
+ * @date 2022-06-20 13:46:12
  */
-@Slf4j
 @Api(tags = "文件服务接口")
 @RestController
-@RequestMapping("/api/file")
-@Deprecated
+@RequestMapping("/api/file/")
 public class FileController {
     private final FileRepository fileRepository;
-    private SpiderFileService spiderFileService;
 
-    public FileController(FileRepository fileRepository, SpiderFileService spiderFileService) {
+    public FileController(FileRepository fileRepository) {
         this.fileRepository = fileRepository;
-        this.spiderFileService = spiderFileService;
-    }
-
-    @ApiOperation("检查文件是否已存在")
-    @GetMapping("/exist")
-    public String isFileExist(@RequestParam("sha256sum") String sha256sum) {
-        return WebBody.success();
     }
 
     @ApiOperation(value = "删除已上传的文件")
@@ -40,4 +28,10 @@ public class FileController {
         String msg = String.format("%s 已删除", uploadId);
         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;
 
 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.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.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.NotNull;
+import java.io.InputStream;
 
 /**
  * @author reghao
- * @date 2022-06-16 10:55:25
+ * @date 2020-03-11 16:10:16
  */
-@Api(tags = "文件服务接口")
+@Slf4j
 @RestController
-@RequestMapping("/api/file/spider")
+@RequestMapping("/api/file/upload/spider")
+@Deprecated
 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();
     }
 }

+ 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);
     }
 
+    @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) {
         String currentUserId = UserContext.getUserId();
         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) {
         return fileInfoMapper.findFileInfoBySha256sum(sha256sum);
     }
@@ -118,8 +147,8 @@ public class FileRepository {
     public void saveFile(String fileId, FileContentType fileContentType, PathUrl pathUrl) throws Exception {
         int fileType = fileContentType.getFileType();
         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());
 
         // 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 {
     private String blockId;
     // 本地文件路径
-    private String filePath;
+    private String absolutePath;
     private String url;
     // 不包括域名的路径
     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;
 
 import lombok.AllArgsConstructor;
+import lombok.Getter;
 
 import java.io.Serializable;
 
@@ -9,6 +10,7 @@ import java.io.Serializable;
  * @date 2022-04-26 15:22:49
  */
 @AllArgsConstructor
+@Getter
 public class UploadFileRet implements Serializable {
     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 java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.EnumSet;
+
+import static java.nio.file.StandardOpenOption.*;
 
 /**
  * @author reghao
@@ -11,11 +16,12 @@ import java.io.*;
  */
 @Service
 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()) {
             FileUtils.forceMkdirParent(file);
-            file.createNewFile();
+            // 创建一个稀疏文件
+            Files.newByteChannel(Paths.get(absolutePath), EnumSet.of(CREATE_NEW, WRITE, SPARSE));
             RandomAccessFile raf = new RandomAccessFile(file, "rw");
             raf.setLength(len);
             raf.close();
@@ -35,8 +41,8 @@ public class FileStoreService {
         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()) {
             return;
         }
@@ -57,8 +63,8 @@ public class FileStoreService {
         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()) {
             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.util.StringUtil;
 import cn.reghao.tnb.file.app.util.media.ImageOps;
+import cn.reghao.tnb.file.app.util.store.LocalStores;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -99,8 +100,15 @@ public class FileUploadService {
                     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);
         return pathUrl;
     }
@@ -125,10 +133,53 @@ public class FileUploadService {
             String fileId = fileMap.getFileId();
             String uploadId = fileMap.getUploadId();
             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);
+            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();
             return new UploadFileRet(uploadId, url);
         }
@@ -162,7 +213,7 @@ public class FileUploadService {
         if (!set.contains(partIndex)) {
             set.add(partIndex);
             long size = fileInfo.getSize();
-            File file = fileStoreService.createFile(pathUrl.getFilePath(), size);
+            File file = fileStoreService.createFile(pathUrl.getAbsolutePath(), size);
             long pos = partIndex * PART_SIZE;
             fileStoreService.writeToFile(multipartFile.getInputStream(), file, pos);
         }
@@ -171,14 +222,14 @@ public class FileUploadService {
         if (len != partNum) {
             return new UploadFilePartRet(fileId, false);
         } else {
-            String path = pathUrl.getFilePath();
+            String path = pathUrl.getAbsolutePath();
             FileInputStream fis = new FileInputStream(path);
             String sha256sumMerged = DigestUtil.sha256sum(fis);
             if (!sha256sum.equals(sha256sumMerged)) {
                 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);
             map.remove(fileId);
             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)
             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 fileDir = storeDir.getFileDir();
+        String fileDir = storeDir.getAbsoluteDirPath();
 
         String filePath = String.format("%s/%s.%s", fileDir, fileId, suffix);
         String url = String.format("//%s/%s", domain, path);
@@ -58,9 +59,10 @@ public class FileUrlService {
     @Deprecated
     public PathUrl getImagePathAndUrl(String sha256sum, String uploadId, long fileSize, String fileId, String suffix,
                                       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 fileDir = storeDir.getFileDir();
+        String fileDir = storeDir.getAbsoluteDirPath();
 
         String filePath = String.format("%s/%s_%sx%s.%s", fileDir, fileId, width, height, 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.StringUtil;
 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.transaction.annotation.Transactional;
 
@@ -41,74 +39,7 @@ public class SpiderFileService {
     }
 
     @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 {
-        String path = pathUrl.getFilePath();
+        String path = pathUrl.getAbsolutePath();
         String url = pathUrl.getUrl();
         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)
     public VideoFile process(String fileId, PathUrl pathUrl) throws Exception {
-        String path = pathUrl.getFilePath();
+        String path = pathUrl.getAbsolutePath();
         String url = pathUrl.getUrl();
 
         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 {
     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 {
         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 nodeIdx = hash % subDirs.size();
-        String dir = subDirs.get(nodeIdx);
-        return new StoreDir(blockId, dir);
+        return null;
     }
 
     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.RoundingMode;
 import java.nio.file.FileStore;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * @author reghao
@@ -20,7 +21,7 @@ public class LocalStore {
     private final String fsType;
     private final String mountedOn;
     private final long total;
-    private volatile long available;
+    private final AtomicLong available;
     private final long max;
 
     public LocalStore(String baseDir, FileStore fileStore, String blockId, double maxPercent) throws IOException {
@@ -31,7 +32,7 @@ public class LocalStore {
         this.fsType = fileStore.type();
         this.mountedOn = fileStore.toString().replace(fs, "").replace(" ()", "");
         this.total = fileStore.getTotalSpace();
-        this.available = fileStore.getUsableSpace();
+        this.available = new AtomicLong(fileStore.getUsableSpace());
 
         BigDecimal bigDecimal1 = new BigDecimal(total*10);
         BigDecimal bigDecimal2 = new BigDecimal(maxPercent*10);
@@ -39,16 +40,16 @@ public class LocalStore {
         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

+ 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.Paths;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @author reghao
  * @date 2022-05-23 18:21:22
  */
 public class LocalStores {
-    private static String domain;
-    @Deprecated
     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 {
-        domain = dfsProperties.getDomain();
         Map<String, String> blockIdMap = getBlockIdMap();
         Map<String, String> checker = new HashMap<>();
         for (String baseDir : dfsProperties.getBaseDirs()) {
@@ -41,57 +38,12 @@ public class LocalStores {
             }
 
             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 {
         Map<String, String> map = new HashMap<>();
         File dir = new File("/dev/disk/by-uuid");
@@ -104,36 +56,63 @@ public class LocalStores {
         return map;
     }
 
-    private static void createSubDirs(String baseDir) throws IOException {
+    private static void createSubDirs(String blockId, String baseDir) throws IOException {
         int total = 128;
         for (int i = 0; i < total; i++) {
             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()) {
                     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) {
-        return storeMap1.get(blockId).getMountedOn();
+        return storeMap.get(blockId).getMountedOn();
     }
 
     public static String getBaseDir(String blockId) {
-        return storeMap1.get(blockId).getBaseDir();
+        return storeMap.get(blockId).getBaseDir();
     }
 
     // TODO 优化算法, 处理异常
     public static LocalStore getMaxStore(long size) {
         Map<String, Long> map = new HashMap<>();
         for (Map.Entry<String, LocalStore> entry : storeMap.entrySet()) {
-            String baseDir = entry.getKey();
+            String blockId = entry.getKey();
             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<>();
@@ -144,11 +123,16 @@ public class LocalStores {
 
         String baseDir = baseDirs.get(baseDirs.size()-1);
         LocalStore localStore = storeMap.get(baseDir);
-        localStore.setAvailable(map.get(baseDir));
+        localStore.setAvail(map.get(baseDir));
         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;
 
 import lombok.AllArgsConstructor;
-import lombok.Getter;
+
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @author reghao
  * @date 2022-05-23 23:31:21
  */
 @AllArgsConstructor
-@Getter
 public class StoreDir {
     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:
   domain: file.reghao.cn
   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();
-    }
-}