分布式文件存储设计
问题
如何设计一个分布式文件存储系统?
答案
架构设计
上传流程
分片上传 + 预签名 URL
@RestController
public class FileController {
// 1. 初始化上传:返回上传凭证
@PostMapping("/upload/init")
public UploadInitVO initUpload(@RequestBody UploadInitRequest req) {
// 秒传检查:根据文件 MD5 查找已上传文件
FileInfo existing = fileMapper.findByMd5(req.getMd5());
if (existing != null) {
return UploadInitVO.quickUpload(existing.getUrl());
}
// 生成分片预签名 URL
String uploadId = ossClient.initiateMultipartUpload(req.getFileName());
List<String> partUrls = new ArrayList<>();
for (int i = 1; i <= req.getPartCount(); i++) {
partUrls.add(ossClient.generatePresignedUrl(uploadId, i));
}
return UploadInitVO.multipart(uploadId, partUrls);
}
// 2. 完成上传:合并分片
@PostMapping("/upload/complete")
public FileInfo completeUpload(@RequestBody UploadCompleteRequest req) {
ossClient.completeMultipartUpload(req.getUploadId(), req.getParts());
FileInfo file = new FileInfo();
file.setFileName(req.getFileName());
file.setMd5(req.getMd5());
file.setSize(req.getSize());
file.setUrl(ossClient.getUrl(req.getFileName()));
fileMapper.insert(file);
return file;
}
}
访问控制
带签名的临时访问 URL
public String getAccessUrl(Long fileId, Long userId) {
FileInfo file = fileMapper.findById(fileId);
// 权限检查
if (!permissionService.canAccess(userId, fileId)) {
throw new ForbiddenException("无权限访问");
}
// 生成 15 分钟有效的签名 URL
return ossClient.generatePresignedUrl(
file.getObjectKey(), 15, TimeUnit.MINUTES
);
}
图片处理
OSS 图片处理参数
// 缩略图:原图 URL + 处理参数
// https://cdn.example.com/img/xxx.jpg?x-oss-process=image/resize,w_200
// 裁剪:?x-oss-process=image/crop,w_100,h_100
// 水印:?x-oss-process=image/watermark,text_xxx
常见面试问题
Q1: 如何实现秒传?
答案:
上传前客户端计算文件 MD5,发给服务端查询。如果 MD5 已存在,直接返回已有文件的 URL,无需实际上传。
Q2: 大文件上传断点续传?
答案:
- 分片上传:文件切为固定大小的分片(如 5MB)
- 每个分片独立上传
- 上传失败只需重传失败的分片
- 全部分片上传完成后调用合并接口
Q3: 为什么用预签名 URL?
答案:
客户端通过预签名 URL 直接上传到 OSS,不经过应用服务器,避免服务器成为带宽瓶颈。预签名 URL 有时效性和权限控制。