96SEO 2026-06-07 08:20 0
说实话,自动驾驶标注文件可不小,几MB到几十MB一大堆JSON。
直接扔到服务器,网络抖动一下就崩了。

所以咱们得把它们切成小块儿,像拼图一样慢慢塞进去。
下面我就用老朋友的口吻给你掰扯掰扯,这玩意儿到底怎么弄。
为什么要分片上传?先别急,我跟你唠叨两句。
大文件一次性传,容易超时。
网络不稳时哪怕丢个包,dou得重头来。
分片后每块儿几百KB,哪怕卡一下也只重传那一块。
还有个好处——断点续传。
下次继续,从上次停的地方接着搞,省事儿!
分片上传的基本思路1)把JSON转成Blob。
2)按固定大小切片。
3)每片带上唯一fileId和序号发给后端。
4)后端收齐后合并成完整文件。
5)合并完再写库或者Zuo后处理。
前端实现:AnnotationUploader 类// AnnotationUploader.js
class AnnotationUploader {
constructor {
// 这里我们默认每片2MB
this.chunkSize = 2 * 1024 * 1024;
}
// 核心方法:上传标注结果
async uploadAnnotation {
// . 将标注数据转为Blob
const blob = new Blob(
,
{ type: 'application/json' }
);
// . 生成唯一标识
const fileId = `annotation_${taskId}_${Date.now}`;
// . 分片上传
return await this.uploadWithChunks;
}
// 分片上传逻辑
async uploadWithChunks {
const totalChunks = Math.ceil;
// 检查Yi上传的分片
const uploaded = await this.getUploadedChunks;
for {
if ) {
console.log;
continue;
}
// 切片
const start = i * this.chunkSize;
const end = Math.min;
const chunk = blob.slice;
// 上传分片
await this.uploadChunk(chunk, {
fileId,
chunkIndex: i,
totalChunks
});
// geng新进度
this.onProgress?.;
}
// 通知服务器合并
return await this.mergeFile;
}
// 上传单个分片
async uploadChunk {
const formData = new FormData;
formData.append;
formData.append;
formData.append;
formData.append;
const response = await fetch('/api/annotation/upload-chunk', {
method: 'POST',
body: formData,
headers: { 'Authorization': `Bearer ${this.getToken}` }
});
if throw new Error;
return response.json;
}
// 查询Yi上传的分片
async getUploadedChunks {
try {
const response = await fetch(
`/api/annotation/upload-status?fileId=${fileId}`,
{ headers: { 'Authorization': `Bearer ${this.getToken}` } }
);
const data = await response.json;
return data.uploadedChunks || ;
} catch { return ; }
}
// 合并文件
async mergeFile {
const response = await fetch('/api/annotation/merge', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.getToken}`
},
body: JSON.stringify
});
return response.json;
}
getToken {
return localStorage.getItem;
}
}
在编辑器里调这个类
// AnnotationEditor.vue
export default {
data {
return {
annotations: {/* ... */},
uploadProgress: 0,
isUploading: false
};
},
methods: {
async saveAnnotations {
this.isUploading = true;
const uploader = new AnnotationUploader;
uploader.onProgress = => {
this.uploadProgress = Math.round;
console.log;
};
try {
const result = await uploader.uploadAnnotation(
this.annotations,
this.annotations.taskId
);
this.$message.success;
console.log;
} catch {
this.$message.error;
// Ke以重试嘛...
console.warn;
//
调用saveAnnotations或提示用户手动重试。
} finally {
this.isUploading = false;
}
},
setupAutoSave {
setInterval => { if this.saveAnnotations; },5*60*1000);
}
},
mounted { this.setupAutoSave; }
};
后端示例:Flask 接收、状态查询、合并
from flask import Flask, request, jsonify
import os,json,shutil
app=Flask
TEMP_DIR='./temp_chunks'
UPLOAD_DIR='./annotations'
@app.route
def upload_chunk:
chunk=request.files
file_id=request.form
chunk_index=int
chunk_dir=os.path.join
os.makedirs
chunk_path=os.path.join
chunk.save
return jsonify
@app.route
def upload_status:
file_id=request.args.get
chunk_dir=os.path.join
if not os.path.exists:
return jsonify
chunks=) for f in os.listdir if f.startswith]
return jsonify})
@app.route
def merge_file:
data=request.json
file_id=data
total_chunks=data
chunk_dir=os.path.join
output_path=os.path.join
with open as out:
for i in range:
chunk_path=os.path.join
with open as cf:
out.write)
# 清理临时目录
shutil.rmtree
# 加载到数据库
with open as f:
anno=json.load
save_to_database
return jsonify
def save_to_database:
# TODO:写入DB逻辑
pass
if __name__=='__main__':
app.run
UI 小组件示例
{{ isUploading ? '保存中...' : '保存标注' }}
继续上传
关键优化点,别忽视!
. 压缩再传
// 使用 pako gzip 压缩 JSON
import pako from 'pako';
const compressed=pako.gzip);
const blob=new Blob;
// 后端记得解压……
. 增量保存,只发改动的帧
// 前端挑出被修改过的帧再发
const changed=this.annotations.frames.filter;
await uploader.uploadAnnotation({taskId:this.taskId,
frames:changed,
isIncremental:true});
. 离线缓存,网络掉线也不慌
// 网络不可用时写 IndexedDB
if{
await saveToIndexedDB;
this.$message.info;
}
window.addEventListener=>{ /* 从缓存恢复 */ });
完整流程概览
标注编辑器 → 点击「保存」 → 构造 JSON → Blob → fileId
↓ 查询服务器Yi有分片
↓ 循环切块 → 上传每块 → 跳过Yi存在块
↓ 所有块结束 → 通知服务器「合并」
↓ 后端读取临时块 → 合并成完整文件 → 写库
↓ 返回成功信息 → 前端弹出提示 🎉
常见坑 & 小贴士
⚡️ 切记别把 chunkSize 写成字符串,要是“2MB”会报错——不对不对,是数字啦。
⚡️ 浏览器内存有限,大文件一次性转 Blob 前Zui好先压缩,否则可Neng卡死页面。
⚡️ 跨域请求忘记加 Authorization,会被拦住。哈哈,这事儿太常见了。
⚡️ 后端合并时一定要按顺序读,否则 JSON 会乱套,你懂的。
⚡️ 用 IndexedDB 存离线数据比 localStorage 大得多,但 API 稍微麻烦点。随便选吧,kan需求。
——别让数据卡住你的车路实验啦!说白了就是把大块儿拆成小砖头,一块一块搬过去,然后再拼回去。
代码里加点压缩、断点续传、离线缓存,你就Neng安心跑千公里的数据标注了。
Ru果哪一步卡住了先kankan控制台日志,再对照上面的代码段——大多数问题dou是参数写错或者忘记开启 CORS。哈哈,你hen快就Neng搞定的!
©2026 自动驾驶技术分享 · 本文仅供学习交流使用作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback