新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java视频分块上传实战:从核心设计到断点续传与秒传实现

发布时间:2026/9/30 3:20:45来源:尧图网络
Java视频分块上传实战:从核心设计到断点续传与秒传实现
这两年教育行业都在搞线上化录播课、微课、直播回放基本成了每个学校的标配。但真正做过教育类网页应用的人都知道最坑的往往不是课程设计和播放器而是最不起眼的“上传视频”。一门课程的视频动不动就是几百MB甚至几个GB用传统的multipart表单整体提交网络稍微抖一下就直接失败用户骂完前端骂后端。Java后端要支持视频文件的分块上传本质上不是为了炫技而是被真实场景逼出来的刚需。这篇文章我按自己在实际项目里的落地经验把分块上传从设计思路、接口定义、Java代码实现到断点续传、秒传、合并校验、常见坑位全流程讲一遍。适合正在做在线教育平台、知识付费网站、内部培训系统的Java工程师参考也适合准备面试时想把这个技术点讲清楚的读者。不管你用的是Spring Boot还是Spring Cloud核心思路是一致的把下面这套方案吃透基本能应对绝大多数教育场景的视频上传需求。1. 为什么视频上传必须走分块这条路1.1 教育场景下的大文件上传困境教育行业的视频文件和普通图片、文档不一样它天然就是“大文件”。一节45分钟的录播课就算压缩成720P少说也要300MB到500MB如果是高清录制的实操课或者双师课堂回放单个文件超过1GB很常见。这类文件如果走传统的一次性POST上传会遇到三个很现实的问题。第一个问题是HTTP连接超时。浏览器和服务器之间的连接不可能无限期保持网关、负载均衡器、浏览器本身都有超时限制。一个500MB的文件在用户上行带宽只有2Mbps的情况下要传半个多小时中间只要断一次前面传的全白费。第二个问题是服务器内存压力传统的Commons FileUpload和Part.write会把整个文件写入临时目录但解析multipart时如果配置不当文件内容被读入JVM堆内存大文件直接触发OOM这是线上事故最常见的来源。第三个问题是无法断点续传用户传了80%断网重新来一遍这个体验在教育行业是要被投诉的。分块上传的思路就是把这个大问题拆成小问题文件在前端切成若干个小块每个块单独上传全部传完后由后端合并成完整文件。任何一个块失败只需要重传那一个块而不是整个文件。这个思路在原理上很像断点下载只不过方向反过来了。1.2 分块上传的核心收益与适用边界分块上传解决的核心是三个问题超时、重传成本和并行效率。切成小块后单次HTTP请求的耗时被控制在几秒到几十秒内大幅降低超时概率某个分块失败后重传成本极低浏览器对同一域名的并发连接数有限制但对同一个文件的多个分块我们可以控制并发数在3到6个之间充分利用用户带宽。不过分块上传也并不是银弹它有一个明显的代价复杂度上来了。涉及前端切片、分块元数据管理、并发控制、合并校验、临时存储清理。如果文件本身只有几MB走分块反而多余直接整包上传更简单。所以你在设计时要有阈值判断逻辑比如小于100MB走整包上传大于100MB走分块上传。教育场景的视频基本都超过这个阈值所以分块是必选项。另外要注意的是分块上传只是解决了上传链路的问题不解决视频处理的问题。教育平台通常还需要在文件合并后进行转码因为网页端播放MP4兼容性最好但录屏软件常输出AVI或MOV生成不同清晰度的版本再切片成HLS供播放器点播。这一部分可以和上传解耦但架构上要提前留好接口。2. 分块上传整体设计与核心细节2.1 前后端交互流程设计在动手写代码之前我强烈建议先把整体流程在纸上画清楚。一个标准的分块上传流程包含三个接口初始化上传、上传分块、合并文件。有些设计会把查询已上传分块也独立成一个接口用来支持断点续传我会在后面单独讲。初始化上传阶段前端在用户选中文件后先向后端发送一个请求携带文件名、文件大小、MD5、分块大小等信息。后端根据MD5判断是否已经存在相同文件如果存在就直接返回“秒传”标识否则生成一个全局唯一的fileId并把上传任务的元数据记录到数据库。这里生成唯一ID不能太随意我建议用UUID去掉横线再拼上时间戳的Base62编码足够短并且不容易撞。上传分块阶段前端按固定大小把文件切片每传一个分块就带着fileId和chunkIndex作为表单字段分块内容本身作为文件字段。后端收到后把分块写入临时目录并更新数据库中的已上传分块信息。这里有个细节分块索引要从前端传过来后端不能依赖文件中继信息去推断序号。合并文件阶段当前端检测到所有分块都已上传成功后调用合并接口。后端按chunkIndex从小到大依次读取临时分块文件写入目标文件。合并完成后计算整个文件的MD5和初始化时记录的MD5比对一致就更新任务状态为成功不一致就标记失败并触发清理。2.2 前端切片参数怎么定分块大小直接影响到上传的成败和效率这个参数不能拍脑袋定。我见过有些项目切2MB一块一个1GB文件就是512块并发6个也得很久而且分块数量太多会导致HTTP请求数量过大服务端IO压力陡增。也有项目切50MB一块这又回到了单请求超时的老问题上。我的经验是分块大小在5MB到20MB之间比较合适推荐10MB。为什么是这个量级首先10MB的块在2Mbps上行带宽下大约耗时40秒在普通4G网络下20秒左右不容易触发超时其次一个1GB文件拆分后是100块这个数量级对数据库记录、失败重试、进度统计来说都很可控。如果用户网络环境特别好比如校园网或企业专线可以适当调大到20MB减少请求数。教育场景的教师用户经常在办公网络下上传稳定性优先我一般固定取10MB。前端切片用Blob对象的slice方法这是HTML5的标准能力兼容性没有问题。代码大致是这样const CHUNK_SIZE 10 * 1024 * 1024; const file document.getElementById(videoFile).files[0]; const totalChunks Math.ceil(file.size / CHUNK_SIZE); let start 0; for (let index 0; index totalChunks; index) { let end Math.min(start CHUNK_SIZE, file.size); const chunk file.slice(start, end); chunks.push({ index, chunk }); start end; }这里要注意一个细节切完片之后每个分块的Blob不能一直被强引用尤其是大文件。如果一次性把所有分块都保存到内存数组里浏览器会直接被拖垮。正确做法是定义一个当前待上传的分块队列传完一个丢弃一个。2.3 服务端存储规划服务端要存的数据有两类一类是每个分块的内容临时存放在磁盘上另一类是上传任务的元数据存在数据库里。分块和最终文件的存储位置要规划清楚不能把分块直接写到最终目录里否则合并时容易被其他逻辑误读。我习惯的目录结构是这样先定义一个根目录比如/data/upload下面分三个子目录temp存放分块final存放合并后的文件failed存放合并失败待人工处理的分块。分块文件名直接命名为fileId_chunkIndex比如6f2a93c1_0、6f2a93c1_1这样合并时排序非常方便。数据库表的字段规划也很重要。上传任务表至少包含fileId、fileName、fileSize、md5、chunkSize、totalChunks、uploadedChunks可以用逗号分隔的字符串存已经上传的分块序号也可以用JSON数组、status、createTime、updateTime。如果分块数量很大用逗号分隔字符串在更新时会反复拼接性能不理想这种情况可以单独建一张分块明细表。但教育场景单个文件分块数量基本在100到300个之间用逗号分隔字符串完全够用少一张表少一堆事务问题。3. 实操落地Java后端关键实现3.1 初始化上传接口我用Spring Boot举例项目结构按controller、service、mapper三层分。初始化上传的Controller接收前端传来的文件名、文件大小、MD5和分块大小生成fileId并写入数据库。PostMapping(/upload/init) public Result initUpload(RequestBody UploadInitRequest request) { // 1. 先查一遍MD5判断是否能秒传 UploadTask existing uploadTaskMapper.findByMd5(request.getMd5()); if (existing ! null existing.getStatus() 2) { return Result.ok(秒传成功, existing.getFileId()); } // 2. 生成fileId String fileId generateFileId(); UploadTask task new UploadTask(); task.setFileId(fileId); task.setFileName(request.getFileName()); task.setFileSize(request.getFileSize()); task.setMd5(request.getMd5()); task.setChunkSize(request.getChunkSize()); task.setTotalChunks( (int) Math.ceil(request.getFileSize() / (double) request.getChunkSize()) ); task.setStatus(0); // 0:初始化 1:上传中 2:完成 3:失败 uploadTaskMapper.insert(task); return Result.ok(初始化成功, fileId); }这里有个判断逻辑很容易被忽视前端传来的fileName是不安全的可能包含路径分隔符或者非法字符后端必须做白名单校验或者重命名。教育平台保存视频时我建议直接以fileId作为存储文件名原始文件名只存数据库用于展示。3.2 分块上传接口分块上传是整个流程中最核心的接口。前端用multipart/form-data提交携带fileId、chunkIndex和分块文件本身。PostMapping(/upload/chunk) public Result uploadChunk(RequestParam(fileId) String fileId, RequestParam(chunkIndex) Integer chunkIndex, RequestParam(file) MultipartFile chunk) { // 1. 校验任务存在且状态正确 UploadTask task uploadTaskMapper.findByFileId(fileId); if (task null || task.getStatus() ! 1) { return Result.error(任务不存在或状态异常); } // 2. 校验chunkIndex范围 if (chunkIndex 0 || chunkIndex task.getTotalChunks()) { return Result.error(分块索引越界); } // 3. 保存分块到临时目录 Path chunkPath Paths.get(tempDir, fileId _ chunkIndex); chunk.transferTo(chunkPath.toFile()); // 注意transferTo内部走的还是IO复制 // 4. 更新已上传分块记录这里用Redis的Set更合适 // 我用数据库时是取出uploadedChunks字符串追加后写回 String uploaded task.getUploadedChunks(); uploaded uploaded null || uploaded.isEmpty() ? String.valueOf(chunkIndex) : uploaded , chunkIndex; uploadTaskMapper.updateUploadedChunks(fileId, uploaded); return Result.ok(分块上传成功); }这条接口看起来简单实际有几个坑。第一个坑是MultipartFile的transferTo方法在Servlet环境下它底层是复制而不是移动分块文件大的话这个方法会同时占用输入输出流的缓冲容易造成内存峰值我建议改用Files.copy(chunk.getInputStream(), chunkPath)显式用InputStream遍历写入可控性更强。第二个坑是上传分块时的任务状态判断我上面写的status必须是1但是初始化后我插进去的status是0前端传第一个分块时会直接校验失败。这里逻辑要调整初始化后直接置为1或者分块上传时只要任务存在且status不等于2都可以接收分块。第三个坑是并发更新uploadedChunks字符串。前端并发上传多个分块时如果都走“取出-拼接-写回”这个逻辑会出现丢失更新。比如chunkIndex为3和4的请求同时到了都读到同一个旧字符串分别拼上3和4后各自写回结果只留下了一个。这个问题有两个解法单机环境下用synchronized或者ConcurrentHashMap做内存锁分布式环境下用Redis的Set类型做分块记录或者用数据库行锁。我实际项目中用的是Redis的sadd命令每收到一个分块就sadd进一个key为fileId的Set里合并时用scard或者smembers判断完整性。这个方案天然支持并发而且合并时还能顺便统计进度。3.3 合并文件接口当前端确认所有分块都上传完毕后会调用合并接口。合并的核心逻辑是按顺序把小文件流拼接到一个大文件里。PostMapping(/upload/merge) public Result mergeUpload(RequestBody MergeRequest request) { String fileId request.getFileId(); UploadTask task uploadTaskMapper.findByFileId(fileId); if (task null) { return Result.error(任务不存在); } // 1. 校验分块完整性 SetInteger uploadedChunks getUploadedChunksFromRedis(fileId); if (uploadedChunks.size() ! task.getTotalChunks()) { return Result.error(分块不完整已上传 uploadedChunks.size() / task.getTotalChunks()); } // 2. 按顺序合并 Path finalPath Paths.get(finalDir, fileId getExtension(task.getFileName())); try (FileChannel out FileChannel.open(finalPath, CREATE, WRITE)) { for (int i 0; i task.getTotalChunks(); i) { Path chunkPath Paths.get(tempDir, fileId _ i); try (FileChannel in FileChannel.open(chunkPath, READ)) { long size in.size(); long transferred in.transferTo(0, size, out); if (transferred ! size) { return Result.error(合并失败分块 i 写入不完整); } } } } // 3. 计算合并文件MD5与初始化时的MD5比对 String finalMd5 md5(finalPath.toFile()); if (!task.getMd5().equalsIgnoreCase(finalMd5)) { task.setStatus(3); uploadTaskMapper.updateStatus(fileId, 3); return Result.error(MD5校验失败文件可能已损坏); } // 4. 更新状态清掉临时分块 task.setStatus(2); uploadTaskMapper.updateStatus(fileId, 2); cleanChunks(fileId, task.getTotalChunks()); return Result.ok(合并成功, finalPath.toFile().getName()); }合并时的FileChannel.transferTo是零拷贝实现在内核态直接复制数据性能比传统的ByteBuffer读写高出很多。这里有一个我在生产环境踩过的坑transferTo在Windows平台上有一个已知限制单次传输超过2GB时可能出错虽然教育场景单文件很少超过2GB但如果平台允许上传超高清长视频建议在循环里判断transferred的大小如果返回0则要用另一种方式传输否则会出现死循环或者文件截断。稳妥的办法是每次transferTo只传实际大小并加一个计数器防止无限循环。合并之后的视频校验除了MD5最好再加一道“文件头校验”。很多教育视频是MP4格式MP4文件的头部包含ftyp标识可以通过读取前8个字节判断是不是合法的MP4。MD5能确认文件内容一致但确认不了文件格式是否可播放文件头校验能更进一步保证合并结果有效。这一步成本很低但能提前拦住一批“文件大小没问题但播放器打不开”的投诉。3.4 服务端并发与线程安全处理教育平台的视频上传通常集中在某个时间段比如开学前一周教师集中上传课件。后端并发处理分块时要特别注意两个层面一是单文件分块之间的并发顺序二是多文件上传任务之间的资源隔离。单文件分块并发也就是同一个fileId的多个分块同时到达Redis的Set操作天然支持并发不会丢数据。但如果用的是数据库存分块记录建议给uploadTask表加上乐观锁版本号或者直接用行锁更新。这个锁只锁同一个fileId的行不会成为全局瓶颈。多文件之间的资源隔离主要靠文件目录设计每个fileId的分块都落在同一个临时目录下文件名以fileId开头天然隔离。更讲究一点可以按fileId的前几个字符建多级子目录比如/temp/6f/2a/fileId_0避免单个目录下文件数量过多导致文件系统性能下降。我见过一个项目把所有文件都堆在一个目录里跑了半年后目录里有几十万个小文件合并时列出目录都卡顿这个教训值得记。3.5 前端上传逻辑与并发控制后端接口就绪后前端要设计一个可靠的上传队列。核心思路是先初始化拿fileId然后把分块数组放进队列启动N个上传Worker并发消费队列每个Worker完成后更新进度条。async function uploadFile(file, fileId, totalChunks) { let uploaded await getUploadedChunks(fileId); // 断点续传时返回已传列表 let queue []; for (let i 0; i totalChunks; i) { if (!uploaded.includes(i)) { queue.push(i); } } const CONCURRENCY 4; let index 0; async function worker() { while (index queue.length) { let chunkIndex queue[index]; let start chunkIndex * CHUNK_SIZE; let end Math.min(start CHUNK_SIZE, file.size); let blob file.slice(start, end); let formData new FormData(); formData.append(fileId, fileId); formData.append(chunkIndex, chunkIndex); formData.append(file, blob, file.name); await axios.post(/upload/chunk, formData); updateProgress(...); } } let workers Array.from({ length: CONCURRENCY }, () worker()); await Promise.all(workers); await axios.post(/upload/merge, { fileId }); }并发数取多少要看用户网络。并发太低浪费带宽太高容易触发路由器或防火墙并发限制。我试过2、4、6、8四个档位在普通家庭宽带上4最稳校园网环境下8也没有问题。一个更聪明的做法是运行时根据单块上传耗时动态调节并发数但这对教育场景有点过度设计固定4就好。进度条的计算也值得写清楚。单块进度是整个文件进度的基础但在实现时要把“已上传分块数”作为主进度而不是看当前正在传的那一块传了多少。比如100个块传了37个整体进度就是37%正在传的第38块的局部进度可以展示在分块明细里但主进度条保持按整块推进。这样用户在网速波动时看到的是稳定的阶梯式进度而不是一下跳一下停体感好很多。4. 断点续传、秒传与轮询设计4.1 断点续传的实现方式分块上传天然支持断点续传这是它比整包上传强一个档次的地方。前端在初始化完成之后可以请求一个查询接口拿到服务端已经收到的分块索引列表然后跳过这些分块只传缺失的部分。查询已上传分块的逻辑非常简单GetMapping(/upload/progress) public Result getProgress(RequestParam(fileId) String fileId) { SetInteger uploadedChunks redisService.smembers(upload:chunks: fileId); UploadTask task uploadTaskMapper.findByFileId(fileId); MapString, Object data new HashMap(); data.put(uploadedChunks, uploadedChunks); data.put(totalChunks, task.getTotalChunks()); data.put(status, task.getStatus()); return Result.ok(data); }前端拿到uploadedChunks后在构造队列时直接过滤掉这样就实现了“接着传”。这里要提醒的是如果用户刷新了浏览器页面file对象还在但内存里的fileId可能已经丢了所以前端在上传前要把fileId存在localStorage或sessionStorage里刷新后重新初始化时如果localStorage里有有效的fileId就不要重新生成而是直接去查进度。初始化接口要支持传入已有fileId做幂等处理。4.2 秒传的判定与实现秒传的核心逻辑是MD5碰撞检测。前端在初始化时把整个文件的MD5发给后端后端查数据库里有没有相同MD5且状态为2的记录如果有说明平台上已经存在一模一样的文件直接返回秒传成功不需要再传任何分块。计算大文件MD5在前端是有成本的一个1GB文件在前端算MD5可能需要几十秒这期间界面无响应。建议前端先把文件算好MD5再进入上传流程并且用一个loading状态提示用户“正在校验文件”。如果觉得前端算MD5太慢也可以退而求其次只取文件的前后各几个KB算指纹但碰撞概率高一些不适合对内容精度要求高的教育平台。我个人的做法是折中先用文件大小做粗筛再用文件头尾块做二次校验最后服务端合并时还会做全量MD5校验三层保险。文件大小和MD5双条件查询时要给数据库加上组合索引否则上传量大了之后秒传接口会慢。4.3 后端定时任务补漏分块上传最头疼的异常是用户上传到一半就关掉了浏览器留下一些分块占着磁盘空间。服务端需要一个定时清理任务定期扫描临时目录里超过一定时限没有更新的分块文件并把对应的数据库记录和Redis key删掉。清理任务要注意不能误删正在上传的文件。通常方案是分块文件的上次修改时间作为判断条件比如超过24小时未更新就认为任务已失效。教育场景下一节课上传不太可能拖24小时这个阈值是安全的。清理时还要先改数据库状态为“已过期”再删文件防止清理过程中用户又传了新的分块。定时任务可以用Spring的Scheduled注解每天凌晨执行一次扫描量控制在全量task表的10%以内避免拖慢正常上传接口。5. 常见问题与排查技巧实录5.1 分块丢失合并时提示“分块不完整”这个问题我在项目里遇到过好几次大多是Redis的key过期导致的。上传过程中的Redis key如果没有设置过期时间Redis的默认淘汰策略在内存紧张时可能把不常用的key清掉合并时smembers查不到完整的分块列表。解决方法是给上传中的Redis key设置一个合理的TTL比如此文件应该在上传的时限内比如24小时同时在合并接口的校验逻辑里如果Redis查不到完整分块可以回退去临时目录统计chunk文件的实际数量以文件系统为准这样即使Redis数据丢失也能精确判断。5.2 合并后视频损坏或无法播放合并之后视频损坏的原因通常有三个。第一是分块顺序错乱用FileChannel合并时排序必须用数值比较而不是字符串比较否则fileId_10会排在fileId_2前面字符串排序会让你怀疑人生。第二是前端切片时没有把最后一个块按实际剩余大小截断我见过有前端代码直接按chunkSize切最后一块越界导致合并时多出一段0字节。第三是并发上传同一个分块后端没有做幂等处理导致文件被覆盖。排查这类问题我建议在合并接口里加审计日志记录每个分块的顺序、大小、MD5合并后把日志和最终MD5放在一起比对缩小问题范围。这是我调试过几次之后总结出来的高效方法空想问题原因浪费时间。5.3 上传高峰期服务器磁盘IO打满教育平台的教师上传高峰期通常在寒暑假前和开学季上传接口的性能瓶颈一般不在CPU而在磁盘。合并大文件时连续读写会让磁盘IO飙升如果分块也被落在同一块磁盘上IO争抢会更严重。优化手段有四个方向分块临时目录和合并最终目录分到不同磁盘合并时限制并行合并任务数量比如用信号量控制在2个以内分块落盘时不需要fsync让操作系统延迟刷盘大文件可以尝试用内存映射但要注意留给JVM的内存要够免得引发GC问题。5.4 转码与上传的衔接问题教育平台的视频上传完成后通常要触发转码任务生成不同清晰度的版本。这里要注意的是转码服务的触发不要放在合并接口里直接调用因为合并接口阻塞太久会影响HTTP响应正确做法是合并成功后往消息队列里发一条转码任务消息由转码Worker异步拉取处理。同时要给任务表加一个转码状态字段前端在上传成功后轮询这个状态显示“上传成功转码中”“转码完成”等不同阶段。不管是RabbitMQ还是RocketMQ这个模式比同步调用健壮得多。写在最后我做过几个教育类的Java后端项目分块上传这个功能让我最大的体会是方案本身不复杂难的是把各种异常情况想全。文件断了、并发撞了、Redis丢了、磁盘满了、转码等了每一环都可能出问题而这些恰恰是面试官最爱问、线上最容易踩的细节。如果你正在做一个教育平台的视频上传功能从分块大小、并发数、Redis记录分块、合并校验、定时清理这几步入手先把主链路打通再逐步加上秒传、断点续传和转码联动。按照我上面说的这套结构来做大概率能少走很多弯路。最后分享一个小技巧上线之前用浏览器的开发者工具把网络切到Slow 3G再传一个500MB以上的视频真实验一遍断网重连、刷新页面续传比你写多少单元测试都管用。教育场景的用户不会按你预设的完美路径操作把坏路走通了就是好系统。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

SQL Server自定义函数实战:用TaoToken统一Key打通AI辅助开发链路 2026/9/30 21:40:13

SQL Server自定义函数实战:用TaoToken统一Key打通AI辅助开发链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Windows 本地 Hermes 完整落地流程:从下载到对话可用分步排坑教程(TaoToken 统一 Key 接入版) 2026/9/30 21:40:13

Windows 本地 Hermes 完整落地流程:从下载到对话可用分步排坑教程(TaoToken 统一 Key 接入版)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
书霸AI科研绘图怎么选?18类图表对比指南 2026/9/30 21:39:21

书霸AI科研绘图怎么选?18类图表对比指南

写论文时,很多人会把“数据已经整理好”等同于“图也能画好”。实际上,同一组数据使用不同图表,表达重点可能完全不同。科研绘图真正困难的地方,往往不是调整颜色和字号,而是判断数据关系、研究目的与图表类型是否匹配…

阅读更多 →
零基础 Vibe Coding 教程:MCP 服务介绍与 TaoToken 统一 Key 接入 2026/9/30 21:39:21

零基础 Vibe Coding 教程:MCP 服务介绍与 TaoToken 统一 Key 接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
一文读懂 MCP:让大模型从“只会回答”到“能解决问题”的核心协议与 TaoToken 配置实践 2026/9/30 21:39:14

一文读懂 MCP:让大模型从“只会回答”到“能解决问题”的核心协议与 TaoToken 配置实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
QClaw 配 TaoToken:微信发句话,自动做 PPT、发邮件、爬数据 2026/9/30 21:39:14

QClaw 配 TaoToken:微信发句话,自动做 PPT、发邮件、爬数据

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉