如何将基于ConcurrentMap锁机制的NFS文件合并方案改成?

摘要:我们在前面已经介绍 《基于ConcurrentMap锁机制的NFS分片上传方案》,今天把上传后的分片文件进行合并。先给大家发一个设计流程图 需要关键的vo类:Chunk(分片文件) 和 FileInfo(合并文件) import org.s
我们在前面已经介绍 《基于ConcurrentMap锁机制的NFS分片上传方案》,今天把上传后的分片文件进行合并。先给大家发一个设计流程图 需要关键的vo类:Chunk(分片文件) 和 FileInfo(合并文件) import org.springframework.web.multipart.MultipartFile; import java.io.Serializable; public class Chunk implements Serializable { private MultipartFile file; private Long id; /** * 当前文件块,从1开始 */ private Integer chunkNumber; /** * 分块大小 */ private Long chunkSize; /** * 当前分块大小 */ private Long currentChunkSize; /** * 总大小 */ private Long totalSize; /** * 文件标识 */ private String identifier; /** * 文件名 */ private String filename; /** * 相对路径 */ private String relativePath; /** * 总块数 */ private Integer totalChunks; /** * 文件类型 */ private String type; } import java.io.Serializable; public class FileInfo implements Serializable { private Long id; private String filename; private String identifier; private Long totalSize; private String type; private String location; } 代码设计关键步骤 初始化NFS客户端 给文件路径上锁 简单用UUID方式生成合并后的文件名 获取上传后的分片文件,并进行分片排序【重要】 try-with-resouces进行文件合并操作 清理分片目录 public Map<String,String> mergeFiles(FileInfo fileInfo) throws IOException { Nfs3 nfs = null; String identifier = fileInfo.getIdentifier(); Map<String,String> result = new HashMap<>(); try { // 初始化NFS客户端并构建路径 nfs = getNfsClient(); Object mergeLock = MERGE_LOCK_MAP.computeIfAbsent(identifier, k -> new Object()); synchronized (mergeLock) { // 准备目录路径 String[] chunkDirPath = {CHUNK_DIR, identifier}; Nfs3File chunkDir = new Nfs3File(nfs, "/"); // 无论NFS_DIR末尾带不带/,这里开头必须/,否者堆栈溢出 // NFS路径规范 // NFS客户端要求每次只操作单级路径 // 当使用new Nfs3File(parent, child)时,child应该是单级目录名(如"chunks"),而不是多级路径(如"chunks/identifier") for (String dir : chunkDirPath) { chunkDir = new Nfs3File(chunkDir, dir); } Nfs3File outputDir = new Nfs3File(nfs, "/"); // 无论NFS_DIR末尾带不带/,这里开头必须/,否者堆栈溢出 //outputDir = new Nfs3File(outputDir, MERGED_DIR); // 创建合并子目录 // ensureDirExists(outputDir); // 创建合并目标文件 // 生成文件UUID名称用于存储 String uuid = UUID.randomUUID().toString().replace("-", ""); String fileName = fileInfo.getFilename(); String ext = fileName.substring(fileName.lastIndexOf(".")); String nfileName = uuid + ext; Nfs3File mergedFile = new Nfs3File(outputDir, nfileName); // 获取排序后的分片列表 List<Nfs3File> sortedChunks = getSortedChunks(chunkDir); // 合并分片内容 try (OutputStream output = new NfsFileOutputStream(mergedFile)) { for (Nfs3File chunk : sortedChunks) { // 4.1 验证分片有效性 if (!chunk.exists()) { throw new IOException("分片文件不存在: " + chunk.getPath()); } // 4.2 流式复制分片内容 try (InputStream input = new NfsFileInputStream(chunk)) { byte[] buffer = new byte[1024 * 1024]; // 1MB缓冲区提升性能 int bytesRead; while ((bytesRead = input.read(buffer)) != -1) { output.write(buffer, 0, bytesRead); } } } } // 清理分片目录 deleteRecursively(chunkDir); // 调用自定义递归删除 // 6. 验证合并结果 if (!mergedFile.exists()) { throw new IOException("合并文件创建失败: " + mergedFile.getPath()); } // return MERGED_DIR + "/" + nfileName; result.put("videoPath", BASE_PATH + "/" + nfileName); result.put("videoUploadName", nfileName); result.put("fileName", fileInfo.getFilename()); return result; } } finally { if (nfs != null) nfs = null; // 清理资源 // 移除锁(允许后续重试) MERGE_LOCK_MAP.remove(identifier); } }