10、《文件上传与下载:MultipartFile与断点续传设计》
文件上传与下载:MultipartFile与断点续传设计
一、基础文件上传与MultipartFile解析
1.1 Spring MVC文件上传基础
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {if (!file.isEmpty()) {try {byte[] bytes = file.getBytes();Path path = Paths.get("uploads/" + file.getOriginalFilename());Files.write(path, bytes);return "redirect:/success";} catch (IOException e) {e.printStackTrace();}}return "redirect:/error";
}
1.2 MultipartFile核心方法解析
public interface MultipartFile {String getName(); // 表单字段名称String getOriginalFilename(); // 原始文件名String getContentType(); // MIME类型boolean isEmpty(); // 是否空文件long getSize(); // 文件字节大小byte[] getBytes() throws IOException;InputStream getInputStream() throws IOException;void transferTo(File dest) throws IOException;
}
二、大文件分片上传设计
2.1 分片上传原理
2.2 前端分片处理示例(JavaScript)
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MBasync function uploadFile(file) {const fileHash = await calculateMD5(file);const chunks = Math.ceil(file.size / CHUNK_SIZE);for (let i = 0; i < chunks; i++) {const chunk = file.slice(i * CHUNK_SIZE, (i+1)*CHUNK_SIZE);const formData = new FormData();formData.append('chunk', chunk);formData.append('chunkNumber', i);formData.append('totalChunks', chunks);formData.append('fileHash', fileHash);await axios.post('/api/upload-chunk', formData);}
}
2.3 后端分片接收处理
@PostMapping("/upload-chunk")
public ResponseEntity<?> uploadChunk(@RequestParam("chunk") MultipartFile chunk,@RequestParam("chunkNumber") int chunkNumber,@RequestParam("totalChunks") int totalChunks,@RequestParam("fileHash") String fileHash) {String tempDir = "temp/" + fileHash + "/";FileUtils.forceMkdir(new File(tempDir));try {chunk.transferTo(new File(tempDir + chunkNumber));return ResponseEntity.ok().build();} catch (IOException e) {return ResponseEntity.status(500).build();}
}
三、断点续传关键技术
3.1 断点续传实现要素
- 唯一文件标识(MD5/SHA1)
- 分片索引记录
- 已上传分片校验
- 分片合并策略
3.2 断点信息存储(Redis示例)
@Service
public class UploadProgressService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final String UPLOAD_PROGRESS_KEY = "upload:progress:";public void saveProgress(String fileHash, Set<Integer> chunks) {redisTemplate.opsForSet().add(UPLOAD_PROGRESS_KEY + fileHash, chunks.toArray(new Integer[0]));}public Set<Integer> getProgress(String fileHash) {return redisTemplate.opsForSet().members(UPLOAD_PROGRESS_KEY + fileHash).stream().map(o -> (Integer)o).collect(Collectors.toSet());}
}
四、OSS云存储集成方案
4.2 阿里云OSS分片上传示例
// OSS配置类
@Configuration
public class OssConfig {@Value("${oss.endpoint}")private String endpoint;@Value("${oss.accessKey}")private String accessKey;@Value("${oss.secretKey}")private String secretKey;@Beanpublic OSS ossClient() {return new OSSClientBuilder().build(endpoint, accessKey, secretKey);}
}// 分片上传服务
@Service
@RequiredArgsConstructor
public class OssUploadService {private final OSS ossClient;public String multipartUpload(String bucketName, String objectName, File file) {InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);String uploadId = result.getUploadId();// 分片上传逻辑List<PartETag> partETags = new ArrayList<>();long contentLength = file.length();int partSize = 5 * 1024 * 1024; // 5MBtry (FileInputStream fis = new FileInputStream(file)) {for(int i = 1; fis.available() > 0; i++) {UploadPartRequest uploadPartRequest = new UploadPartRequest();uploadPartRequest.setBucketName(bucketName);uploadPartRequest.setKey(objectName);uploadPartRequest.setUploadId(uploadId);uploadPartRequest.setInputStream(fis);uploadPartRequest.setPartSize(partSize);uploadPartRequest.setPartNumber(i);UploadPartResult uploadResult = ossClient.uploadPart(uploadPartRequest);partETags.add(uploadResult.getPartETag());}CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);ossClient.completeMultipartUpload(completeRequest);return objectName;}}
}
五、完整项目目录结构
file-upload-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── config/
│ │ │ │ └── OssConfig.java
│ │ │ ├── controller/
│ │ │ │ └── FileController.java
│ │ │ ├── service/
│ │ │ │ ├── UploadService.java
│ │ │ │ └── impl/
│ │ │ │ ├── LocalUploadServiceImpl.java
│ │ │ │ └── OssUploadServiceImpl.java
│ │ │ ├── util/
│ │ │ │ └── FileUtils.java
│ │ │ └── FileUploadDemoApplication.java
│ │ └── resources/
│ │ ├── application.yml
│ │ └── static/
├── pom.xml
└── README.md
六、性能优化建议
- 动态分片大小调整(网络质量检测)
- 并行分片上传(前端Promise.all)
- 客户端计算文件指纹(Web Worker)
- 分片完整性校验(SHA-256)
- 自动重试机制(指数退避策略)
总结
本文从基础文件上传实现出发,深入剖析了分片上传与断点续传的核心技术,并结合云存储服务给出了企业级解决方案。示例代码可直接集成到Spring Boot项目中,建议根据实际业务需求调整分片策略和存储方案。