跳到主要内容

分布式文件存储设计

问题

如何设计一个分布式文件存储系统?

答案

架构设计

上传流程

分片上传 + 预签名 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 有时效性和权限控制。

相关链接