package com.ard.inspection.service.impl; import com.ard.common.core.domain.AjaxResult; import com.ard.inspection.domain.ArdPointCloud; import com.ard.inspection.domain.UploadFileEntity; import com.ard.inspection.service.IArdPointCloudService; import com.ard.inspection.service.IBigFileUploadService; import com.ard.inspection.utils.ZipFileUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.*; import java.util.stream.Collectors; import static com.ard.inspection.utils.ZipFileUtils.formatFileSize; /** * @author xia */ @Service @Slf4j public class BigFileUploadServiceImpl implements IBigFileUploadService { @Value(value = "${pointCloud.url}") private String cloudUrl; @Autowired private IArdPointCloudService ardPointCloudService; @Resource private ZipFileUtils zipFileUtils; @Override public Map checkChunk(String fileMd5,String chunkPath) { Map result = new HashMap<>(); try { File chunkFolder = new File(chunkPath + File.separator + fileMd5); if (!chunkFolder.exists()) { result.put("exception", "分片不存在!!!"); return result; } File[] chunks = chunkFolder.listFiles(); if (chunks == null || chunks.length == 0) { result.put("exception", "无分片!!!"); return result; } List uploadedChunks = Arrays.stream(chunks) .map(file -> Integer.parseInt(file.getName().split("\\.")[0])) .collect(Collectors.toList()); result.put("message", uploadedChunks); return result; } catch (Exception e) { log.error("检查分片失败", e); result.put("exception", "检查分片失败!!!"); return result; } } @Override public Map uploadChunk(MultipartFile file, Integer chunkNumber, String fileMd5, String chunkPath) { Map result = new HashMap<>(); try { // 验证参数 if (file.isEmpty()) { result.put("exception", "文件不能为空!!!"); return result; } // 创建分片目录 String chunkDirPath = chunkPath + File.separator + fileMd5; File chunkDir = new File(chunkDirPath); if (!chunkDir.exists()) { chunkDir.mkdirs(); } // 保存分片 String chunkFileName = chunkNumber + ".part"; File chunkFile = new File(chunkDir, chunkFileName); file.transferTo(chunkFile); result.put("message", "分片上传成功!!!"); return result; } catch (IOException e) { log.error("分片上传失败", e); result.put("exception", "分片上传失败!!!"); return result; } } @Override public Map uploadChunk(MultipartFile file,UploadFileEntity uploadFileEntity,String chunkPath) { Map result = new HashMap<>(); try { // 验证参数 if (file.isEmpty()) { result.put("exception", "文件不能为空!!!"); return result; } // 创建分片目录 String chunkDirPath = chunkPath + File.separator + uploadFileEntity.getFileMd5(); File chunkDir = new File(chunkDirPath); if (!chunkDir.exists()) { chunkDir.mkdirs(); } // 保存分片 String chunkFileName = uploadFileEntity.getChunkNumber() + ".part"; File chunkFile = new File(chunkDir, chunkFileName); file.transferTo(chunkFile); result.put("message", "分片上传成功!!!"); return result; } catch (IOException e) { log.error("分片上传失败", e); result.put("exception", "分片上传失败!!!"); return result; } } @Override public Map mergeChunks(String md5,String fileName,String uploadPath) { Map result = new HashMap<>(); // 临时目录 String chunkDir = uploadPath + File.separator + md5; File dir = new File(chunkDir); if (!dir.exists()) { result.put("exception", "切片目录不存在"); return result; } // 获取所有切片文件 File[] chunks = dir.listFiles(); if (chunks == null || chunks.length == 0) { result.put("exception", "没有切片文件"); return result; } // 按照切片索引排序 Arrays.sort(chunks, (a, b) -> { int aIndex = Integer.parseInt(a.getName().split("\\.")[0]); int bIndex = Integer.parseInt(b.getName().split("\\.")[0]); return aIndex - bIndex; }); // 合并后的文件,这里使用原始文件名,为了避免重名,可以加上时间戳或使用UUID // 这里根据业务需求,可以保存到数据库,记录文件路径等信息 String ext = fileName.substring(fileName.lastIndexOf(".")); String newFileName = UUID.randomUUID().toString().replace("-", "") + ext; // 生成新文件名 File mergeFile = new File(uploadPath, newFileName); try (RandomAccessFile randomAccessFile = new RandomAccessFile(mergeFile, "rw")) { byte[] buffer = new byte[1024 * 1024]; // 1MB缓冲区 for (File chunk : chunks) { try (RandomAccessFile chunkFile = new RandomAccessFile(chunk, "r")) { int len; while ((len = chunkFile.read(buffer)) != -1) { randomAccessFile.write(buffer, 0, len); } } // 删除切片文件 chunk.delete(); } // 删除临时目录 dir.delete(); } catch (IOException e) { log.error("合并文件失败", e); result.put("exception", "合并文件失败"); return result; } // 这里可以保存文件信息到数据库,例如文件路径、文件名、md5等 // fileService.saveFileInfo(...); // ArdPointCloud ardPointCloud = new ArdPointCloud(); // ardPointCloud.setFileName(newFileName); // ardPointCloud.setFileSize(mergeFile.length()); // ardPointCloud.setAssociatedState("0"); // ardPointCloudService.insertArdPointCloud(ardPointCloud); result.put("message", "文件合并成功"); return result; } /** * 大文件上传并解压接口 * @param file 上传的ZIP文件 * @param overwrite 是否覆盖已存在文件(默认false) * @param uploadPath 上传文件存储路径 * @param chunkPath 临时切片存储路径 * @param zipPath 解压文件存储路径 * @return 上传结果 */ @Override public AjaxResult uploadBigZipFile(MultipartFile file, boolean overwrite, String uploadPath, String chunkPath, String zipPath) { // 1. 校验文件是否为空 if (file.isEmpty()) { return AjaxResult.error("上传文件不能为空"); } // 2. 校验文件格式 String originalFilename = file.getOriginalFilename(); if (originalFilename == null || !originalFilename.endsWith(".zip")) { return AjaxResult.error("仅支持ZIP格式文件上传"); } // 2.1 校验文件名是否重复 if (!overwrite) { if (ardPointCloudService.selectArdPointCloudByFileName(originalFilename) != null) { return AjaxResult.error("文件已存在"); } } // 3. 创建临时目录 File tempDir = new File(uploadPath); if (!tempDir.exists()) { boolean mkdirs = tempDir.mkdirs(); if (!mkdirs) { return AjaxResult.error("创建临时上传目录失败"); } } // 4. 保存上传的ZIP文件到临时目录(避免重名) String tempFileName = UUID.randomUUID() + ".zip"; File tempFile = new File(chunkPath + File.separator + tempFileName); try { file.transferTo(tempFile); log.info("临时文件保存成功:{},文件大小:{}MB", tempFile.getAbsolutePath(), file.getSize() / 1024 / 1024); } catch (IOException e) { log.error("保存临时文件失败", e); return AjaxResult.error("文件上传失败:" + e.getMessage()); } // 5. 解压文件 try { zipFileUtils.unzip(tempFile.getAbsolutePath(), overwrite); // 6. 删除临时文件(可选,若需要保留压缩包可注释) boolean delete = tempFile.delete(); if (delete) { log.info("临时文件已删除:{}", tempFile.getAbsolutePath()); } ArdPointCloud ardPointCloud = new ArdPointCloud(); ardPointCloud.setFileName(originalFilename); ardPointCloud.setFileSize(formatFileSize(file.getSize())); int dotIndex = originalFilename.lastIndexOf('.'); String folderName = (dotIndex > 0) ? originalFilename.substring(0, dotIndex) : originalFilename; String cloudType = "/tileset.json"; String zipP = cloudUrl + folderName + cloudType; ardPointCloud.setFilePath(zipP); ardPointCloud.setFolderName(folderName); return AjaxResult.success("文件上传并解压成功", ardPointCloud); } catch (IOException e) { log.error("解压文件失败", e); // 失败时删除临时文件 if (tempFile.exists()) { tempFile.delete(); } return AjaxResult.error("文件解压失败:" + e.getMessage()); } } }