📰 来源: 博客园
下班前,监控系统给我发了条告警:NAS 存储空间不足 10%。
打开后台一看,500G 的硬盘塞得满满当当,全是这两年积累的项目文档、设计稿、测试数据。清理了一波,也就腾出 20G,治标不治本。
买新硬盘?现在存储价格涨到天际,一块 2T 的企业级硬盘要 1000+。上云存储?阿里云 OSS 标准存储 500G 一年要 480 元,加上流量费、请求费,一年下来得好几百。
正发愁呢,同事发来一条飞书消息,附带一个云文档链接。我点开一看,突然意识到一个问题:
飞书企业版,每个用户有 100G 云空间,完全免费!
我们公司 50 个人,那就是 5T 的免费存储空间!
一个大胆的想法冒了出来:能不能把飞书云盘当成免费网盘用?
飞书云存储的"白嫖"姿势
先搞清楚飞书云盘的规则
在动手之前,先把飞书云盘的"家底"摸清楚:
⚠️ 注意:以上是企业版(标准版)的配置,具体以你企业的实际套餐为准。
这个系统能帮你省多少钱
光说不练假把式,来算笔账:
一年省下 700+,这钱拿来喝奶茶不香吗?
当然,直接用飞书 App 上传文件不是我们的目标。我们要做的是一个独立的文件管理系统,把飞书云盘当底层存储:
先上成果:白嫖成功了!
口说无凭,先看看做出来的效果:
系统已经跑起来了,下面我就一步步讲怎么实现的。
上传:把文件"搬"到飞书云盘
普通上传 vs 分片上传
飞书对单文件有 500MB 的限制,但我们实际使用中经常遇到几百兆的设计稿、视频素材。怎么破?分片上传。
切片逻辑很简单,用 JavaScript 的 Blob.slice() 方法:
// ChunkUploader.vue - 核心切片逻辑
const CHUNK_SIZE = 5 * 1024 * 1024; // 每片 5MB
async function uploadFile(file) {
// 1. 计算文件MD5(用于秒传和校验)
const fileHash = await calculateMD5(file);
// 2. 切片
const chunks = [];
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
for (let i = 0; i < totalChunks; i++) {
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
chunks.push({
index: i,
blob: file.slice(start, end),
size: end - start
});
}
// 3. 初始化分片上传
const { uploadId } = await api.initChunkUpload({
fileName: file.name,
fileSize: file.size,
fileHash,
totalChunks
});
// 4. 逐片上传(支持并发)
for (const chunk of chunks) {
await api.uploadChunk(uploadId, chunk.index, chunk.blob);
updateProgress(chunk.index / totalChunks * 100);
}
// 5. 完成上传
await api.completeChunkUpload(uploadId);
}
后端收到分片后,调用飞书 API 完成最终合并:
// ChunkUploadService.cs - 分片合并
public async Task<CompleteResult> CompleteUploadAsync(string uploadId)
{
// 1. 获取所有分片信息
var chunks = await _dbContext.ChunkUploadRecords
.Where(c => c.UploadId == uploadId)
.OrderBy(c => c.ChunkIndex)
.ToListAsync();
// 2. 校验分片完整性
if (chunks.Count != chunks.First().TotalChunks)
{
throw new InvalidOperationException("分片不完整");
}
// 3. 调用飞书API合并分片
var response = await _driveFiles.UploadCompleteAsync(new UploadCompleteRequest
{
UploadId = uploadId,
BlockNum = chunks.Count,
BlockSize = CHUNK_SIZE
});
// 4. 创建文件记录
var fileRecord = new FileRecord
{
FileToken = response.Data.FileToken,
FileName = chunks.First().FileName,
FileSize = chunks.Sum(c => c.ChunkSize),
UploadTime = DateTime.UtcNow
};
_dbContext.FileRecords.Add(fileRecord);
// 5. 清理临时分片记录
_dbContext.ChunkUploadRecords.RemoveRange(chunks);
await _dbContext.SaveChangesAsync();
return new CompleteResult { Success = true, FileToken = fileRecord.FileToken };
}
一个隐藏坑:分片过期时间
开发过程中踩了个坑:飞书的分片上传有 7 天有效期。
📌 场景:用户上传一个 300MB 文件,传到一半断网了,过了 8 天再来续传,结果报错"分片已过期"。
解决方案:前端记录上传进度到 localStorage,每次打开页面检查是否有未完成的上传任务:
// 检查断点续传
function checkPendingUploads() {
const pending = localStorage.getItem('pending_uploads');
if (pending) {
const uploads = JSON.parse(pending);
// 过滤掉超过6天的(留1天余量)
const valid = uploads.filter(u =>
Date.now() - u.startTime < 6 * 24 * 60 * 60 * 1000
);
return valid;
}
return [];
}
同步:让飞书云盘变成你的"第二硬盘"
用户可能会问:
🔗 原文链接: 点击阅读原文
文章评论