新闻详情

新闻详情

首页 / 资讯中心 / 详情

Vue3 + Element Plus 图片直传 OSS:从签名到回显的完整实践

发布时间:2026/10/2 10:08:49来源:尧图网络
Vue3 + Element Plus 图片直传 OSS:从签名到回显的完整实践
光把 el-upload 接上不算完麻烦都在后头跨域、签名、回显、重复上传、CORS 报错任何一个都能卡你半天。这篇文章就是我自己在 Vue3 Element Plus 项目里把图片上传到 OSS 的完整记录从思路拆解到关键代码再到我踩过的几个坑一次性讲清楚希望你看完能少走弯路。1. 先理清整体思路直传 OSS 还是走服务端中转1.1 最常见的两种上传链路很多朋友第一次做上传功能脑子里浮现的就是 el-upload 的action属性把接口地址直接填进去。但这里有个核心问题上传到 OSS 和上传到自己的服务器是两码事。如果填的是自己后端接口那是后端帮你收文件再转存到 OSS属于服务端中转如果想让文件不经过你的应用服务器、直接进 OSS 桶就需要前端直传。服务端中转的流程大致是前端选文件请求后端接口后端接收文件流再调用 OSS SDK 写入存储桶最后把文件 URL 返回给前端。这样做的好处是前端代码简单el-upload 几乎不用改action直接指后端接口就行坏处是文件流量全都经过你的服务器大文件、高并发场景下服务器带宽和内存会被消耗得很厉害。前端直传的流程则是前端先请求后端一个签名接口拿到 OSS 的上传地址和临时凭证然后在浏览器端把文件用表单格式直接提交给 OSS。文件不经过你的应用服务器压力小很多速度也快但相应的签名接口、表单字段、CORS 配置这些都得自己搞定el-upload 的默认行为也要改。1.2 我为什么最终选前端直传我这次的需求是后台管理系统的图片上传图片大小一般在几百 KB 到几 MB 之间用户是内部运营人员并发量不大。乍一看服务端中转好像也能扛但我还是选了前端直传主要基于三点考虑。第一文件流量不进应用服务器。OSS 本身带宽便宜回源速度和 CDN 结合也快而应用服务器带宽是要花钱的没必要为一个上传功能扛所有图片流量。第二上传速度更快。浏览器是直接和 OSS 的域名通信少一跳网络转发大文件上传体感明显比走中转快。第三代码自由度更高。前端可以自己控制上传进度、分片策略、失败重试不用依赖后端接口的能力范围。当然直传也不是没有代价。最直接的一个问题就是必须有一个后端签名接口否则任何前端直传都是裸奔。安全和不安全的分水岭就在这里——如果你把 AccessKeyId 和 AccessKeySecret 写死在前端代码里那你等于把存储桶的钥匙交给了任何人别人拿到之后可以随意往你桶里传文件、覆盖文件甚至把你整个桶塞满欠费。我见过不止一次这种事故所以下面说的签名方案千万不能省。1.3 前端直传与后端中转的对比对比项服务端中转前端直传签名后上传应用服务器压力高文件流经服务器低文件直达 OSS前端复杂度低action 指向后端接口中需要自定义逻辑上传速度受服务器带宽限制受客户端上行带宽限制一般更快安全性依赖后端鉴权较易控制依赖签名接口和临时凭证需规范设计大文件支持服务器容易超时可配合分片上传体验更好适用场景文件需后端实时处理、鉴权严格常规图片/文件上传追求速度和成本看完这个对比你应该能理解中小型后台功能我建议直接上直传早点把链路上的坑趟完后面扩展分片、断点续传都顺手。2. 实操前的准备签名接口与关键参数2.1 签名接口需要返回什么直传 OSS 并不是前端自己造一个请求就能上传的你要让 OSS 知道你这请求是合法授权的。正规做法是后端用 AccessKeySecret 对上传策略进行签名然后返回一组参数给前端前端拿这组参数去 POST。这组参数就是 OSS 直传的“通行证”。我后端返回的 JSON 结构大致是这样{ accessId: STS 临时密钥的 AccessKeyId, policy: Base64 编码后的上传策略, signature: 签名串, dir: 上传目录比如 uploads/2025/04/, host: https://your-bucket.oss-cn-hangzhou.aliyuncs.com, expire: 签名过期时间戳 }其中policy是核心。它是一段 JSON 文本描述了这个签名允许用户做什么、上传到哪个目录、过期时间是什么时候然后做 Base64 编码。后端生成policy的逻辑大致是const policy Buffer.from(JSON.stringify({ expiration: expireDate, // 签名过期时间一般10~30分钟 conditions: [ { bucket: bucketName }, [starts-with, $key, dir] // 文件路径必须以 dir 开头 ] })).toString(base64);最关键的就是这个[starts-with, $key, dir]它表示所有通过这个签名上传的文件其 object key可以理解为 OSS 里的文件路径都必须以某个前缀开头。这样一来用户不能随意往别的目录传文件也不能覆盖其他路径下的文件。后端还会生成一个signature用 HMAC-SHA1 算法对 policy 做签名。前端不需要关心算法细节只需要把这个值原样放进表单里一起发给 OSS 就行。获取签名的接口一定要带鉴权。这个接口通常不应匿名开放至少得登录态校验不然别人拿你这个接口一刷就能获取合法签名配合直传就能往你桶里传任意文件了。我项目里给这个接口加了登录态拦截并且控制了单次签名的有效时间防止签名被长时间复用。2.2 前端组装 formData 的核心字段拿到签名接口的返回值之后前端要做的事就是把文件和一个固定结构的表单数据一起 POST 到 OSS。这里有个知识点OSS 直传的 POST 表单字段和普通 multipart 上传不一样顺序和拼写都不能错。标准的结构如下const formData new FormData(); formData.append(key, fileName); // OSS 里的完整文件路径比如 uploads/2025/04/xxx.jpg formData.append(policy, res.data.policy); formData.append(OSSAccessKeyId, res.data.accessId); formData.append(success_action_status, 200); formData.append(signature, res.data.signature); formData.append(file, file, originalFileName); // 真正的文件key就是最终文件保存在 OSS 上的路径。我之前有朋友在这里踩坑直接用了文件名当key结果所有人的头像都叫avatar.png后上传的就把前上传的覆盖了。正确的做法是让后端返回目录前缀dir前端自己拼一个唯一文件名比如把dir uuid 文件后缀拼起来作为key。success_action_status这个字段挺关键它表示 OSS 在文件上传成功之后返回给客户端的 HTTP 状态码。默认情况下如果不指定OSS 返回的是 204但 204 在浏览器处理时不会有响应体很多时候你很难从返回内容里拿到文件 URL。指定成200之后OSS 会返回一个 XML 格式的响应体里面包含最终文件的存放地址。这个 200 是我实测下来最省事的配置。2.3 文件名规则与防覆盖这一小节不写代码但非常重要。OSS 是对象存储不是传统文件系统同名文件覆盖是不可逆的你要么接受覆盖要么在源头上杜绝。我常用的命名方案是${dir}${YYYYMMDD}/${随机字符串}.${后缀}。随机字符串推荐用 UUID 的前 8 位加上时间戳既能保证可读性又基本不可能冲突。需要注意这里不能用 Date.now() 作为完整文件名——同一毫秒内并发上传两张图就会把前一张覆盖掉。我见过这种显眼包事故上传两张图打开相册一看只有一张就是文件名生成逻辑只用了时间戳。如果你是需要固定文件名覆盖的场景比如用户上传头像永远只想存一份那检查一下 OSS 服务端有没有开启版本控制。没开版本控制的话覆盖就是永久性的前端代码层能做的最佳实践也只是在上传前把旧的删除或者直接顺其自然。3. el-upload 核心配置与代码实现3.1 模板结构el-upload 本身提供了一套完整的生命周期选文件、上传中、成功、失败。我们其实不需要改动它的行为逻辑只需要接管它内部真正发请求的那一步。这一步的官方接口就是http-request属性你传一个自定义函数进去它就会完全替代默认的 XHR 上传。拿到这个入口直传 OSS 的代码基本上就成型了。我写的模板长这样template el-upload refuploadRef action# :http-requesthandleUploadRequest :before-uploadhandleBeforeUpload :on-errorhandleUploadError :on-successhandleUploadSuccess :show-file-listfalse acceptimage/png, image/jpeg, image/webp :disableduploading el-button typeprimary :loadinguploading上传图片/el-button template #tip div classel-upload__tip支持 JPG/PNG/WebP大小不超过 5MB图片将保存至 OSS/div /template /el-upload /template几个容易忽略的点action属性即使不用也要给一个占位值因为 el-upload 内部校验这个属性是否为空空字符串可能导致组件行为异常。我给的是#反正最终请求会被http-request接管。show-file-list我设置成了 false。这个选项作用是不显示 el-upload 自带的文件列表 UI因为后台系统通常需要自定义预览效果用自带列表很难做好样式统一。如果你没有自定义预览需求保持 true 也行但要注意文件列表里展示的名称默认来自file.name不是 OSS 的路径。accept属性只是文件选择器的一个过滤器选项它通过 MIME type 过滤用户能选中的文件类型但不是硬性拦截。要真正做到类型限制要在before-upload里再校验一次那个才是前端逻辑层面的拦截。3.2 http-request 自定义上传方法这是整个组件最核心的方法签名格式固定为async (options) Promisevoid。官方文档说http-request接收一个 options 对象里面有file、onProgress、onSuccess、onError等方法但 typeScript 类型定义不够友好经常要用解构 断言的方式处理。我封装的完整代码如下import { ElMessage } from element-plus; import { ref } from vue; import { getOssPolicy } from /api/oss; const uploading ref(false); interface UploadRequestOptions { file: File; onProgress?: (event: { percent: number }) void; onSuccess?: (response: unknown) void; onError?: (error: Error) void; } async function handleUploadRequest(options: UploadRequestOptions) { const { file, onProgress, onSuccess, onError } options; try { uploading.value true; // 1. 获取签名 const policyData await getOssPolicy(); // 2. 组装文件路径 const suffix file.name.split(.).pop() || ; const fileName ${policyData.dir}${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${suffix}; // 3. 组装 formData const formData new FormData(); formData.append(key, fileName); formData.append(policy, policyData.policy); formData.append(OSSAccessKeyId, policyData.accessId); formData.append(success_action_status, 200); formData.append(signature, policyData.signature); formData.append(file, file); // 4. 发起上传请求这里需要规避 mock、baseURL、全局拦截器等干扰 const xhr new XMLHttpRequest(); xhr.open(POST, policyData.host); xhr.upload.onprogress (e) { if (e.lengthComputable onProgress) { onProgress({ percent: Math.round((e.loaded / e.total) * 100) }); } }; xhr.onload () { if (xhr.status 200) { // OSS 返回 XML可以直接拿返回结果里的 Key 拼 URL const result { url: ${policyData.host}/${fileName}, }; onSuccess?.(result); } else { onError?.(new Error(上传失败HTTP ${xhr.status})); } }; xhr.onerror () onError?.(new Error(网络异常上传失败)); xhr.send(formData); } catch (err) { onError?.(err as Error); } finally { uploading.value false; } }这里我绕开了 axios直接用 XMLHttpRequest。原因很现实项目里的 axios 实例大多配置了baseURL和响应拦截器而 OSS 的 host 是一个绝对地址一旦被 axios 的拦截器处理要么路径拼错要么响应格式被强行解析反而添乱。XHR 是最原始也最可控的提交方式几行代码就能完成推荐大家也这么做。Math.random().toString(36).slice(2, 10)是生成随机字符串的一种取巧方式输出通常 8 位左右足以应付普通业务量。如果你对命名冲突特别敏感建议用crypto.randomUUID()兼容性和安全性都更好。3.3 上传进度与成功回调进度条方面el-upload 默认的on-progress回调会收到一个进度事件但那个进度是基于它内部 XHR 的实现。我们自定义了http-request之后进度事件需要自己手动触发触发方式就是调用options.onProgress并传入包含percent属性的对象el-upload 才能更新它内部的进度状态。如果你只是给按钮加loading状态不显示进度条那这一步可以简化不触发onProgress只在finally里关掉 loading。但如果你想在页面上展示真实的百分比就要监听xhr.upload.onprogress事件把loaded / total计算成百分比再通过onProgress传出去。注意这里loaded和total单位是字节计算之后要Math.round一下避免进度条上出现一堆小数。成功回调的逻辑我写得比较粗暴直接把host fileName拼成 URL 返回。OSS 直传成功之后返回的 XML 里其实包含Location和Key节点Location就是文件的完整 URL。但从 XML 里解析节点要引入额外库前端拿host fileName自己拼反而更简单。前提是你的 OSS bucket 权限是公共读或者文件 URL 需要走 CDN 加速时用 CDN 域名替换host就行const result { url: ${cdnDomain ?? policyData.host}/${fileName}, };这个 CDN 域名建议用变量维护开发环境和生产环境可能不同不要写死在代码里。3.4 上传成功后的回显与表单提交上传成功后我们拿到的是一个 OSS URL。后台系统的常规做法是把 URL 存进表单数据模型提交表单时连同其他业务字段一起发送给后端后端直接拿这个 URL 入库。也就是说图并没有经过后端但 URL 经过后端。我的表单里定义了一个coverUrl字段成功回调里把它赋值同时用一张el-image预览template div classupload-wrapper el-upload action# :http-requesthandleUploadRequest :before-uploadhandleBeforeUpload :on-successhandleUploadSuccess :on-errorhandleUploadError :show-file-listfalse template v-ifcoverUrl el-image :srccoverUrl fitcover classpreview-image / div classmask点击更换/div /template template v-else el-iconPlus //el-icon div点击上传/div /template /el-upload /div /template script setup langts const coverUrl ref(); function handleUploadSuccess(response: { url: string }) { coverUrl.value response.url; } function handleUploadError() { ElMessage.error(上传失败请检查网络或联系管理员); } /script注意一个细节el-upload 的on-success回调签名是(response, file, uploadFiles) void其中response就是我们自己传出去的onSuccess的值。所以自定义上传方法里onSuccess?.(result)传了什么handleUploadSuccess的第一个参数就能接收什么完全可以自定义。4. 不能少的细节类型校验、大小限制、多图处理4.1 before-upload 的校验要点before-upload是 el-upload 提供给我们的最后一个拦截点如果返回false或者返回一个 reject 的 Promise上传流程会被终止。这个钩子的执行时机在文件选择之后、调用http-request之前非常适合做统一的文件校验。我写的校验函数是三个条件function handleBeforeUpload(file: File) { const allowedTypes [image/jpeg, image/png, image/webp]; const maxSize 5 * 1024 * 1024; // 5MB if (!allowedTypes.includes(file.type)) { ElMessage.error(仅支持 JPG、PNG、WebP 格式的图片); return false; } if (file.size maxSize) { ElMessage.error(图片大小不能超过 5MB); return false; } return true; }有一点必须提file.type判断并不绝对可靠某些环境下比如部分 Mac 浏览器文件类型可能为空字符串。如果你遇到过这种情况可以退一步用文件后缀名兜底const ext file.name.split(.).pop()?.toLowerCase() ?? ; const allowedExts [jpg, jpeg, png, webp]; if (!allowedExts.includes(ext)) { ElMessage.error(仅支持 JPG、PNG、WebP 格式的图片); return false; }两个策略可以双保险先用 type 判断再对无法识别 type 的文件用 ext 判断。4.2 多图上传与文件列表管理如果业务需要一次上传多张图最直接的做法是把上面的逻辑做成一个组件然后循环渲染多个 el-upload。但更常见的需求是一张表单里上传多张图片并允许删除。这个时候file-list的管理就派上用场了。我的方案是用一个imageList数组维护 URL 列表el-upload 设置show-file-list为 false自己用v-for渲染。template div classimage-list div v-for(url, index) in imageList :keyurl classimage-item el-image :srcurl fitcover / el-button typedanger sizesmall clickhandleRemoveImage(index)删除/el-button /div el-upload action# :http-requesthandleUploadRequest :show-file-listfalse :on-successhandleUploadSuccess el-button新增图片/el-button /el-upload /div /template script setup langts const imageList refstring[]([]); function handleUploadSuccess(response: { url: string }) { imageList.value.push(response.url); } function handleRemoveImage(index: number) { imageList.value.splice(index, 1); const key decodeKeyFromUrl(imageList.value[index] ?? ); // 视业务情况决定是否删除 OSS 上的文件 } /script这里提示一下删除图片有两种策略。一种只是从前端列表移除OSS 上的文件还在后续可以靠后端的任务去清理。另一种是前端调用后端删除接口后端用 OSS SDK 删除对应 object。第二种要特别注意鉴权和控制权限不要开放成任意 object key 都能删除的接口否则别人拿到接口可以把你的桶清空。我这边的做法是后端根据当前登录用户的业务资源 ID 校验 key 是否属于本人再执行删除。4.3 编辑场景下的回显处理编辑场景和新增场景有一个不同的关键点进入编辑页时表单里的图片 URL 是后端返回的不是新上传的。此时如果用户不换图直接点保存后端能正常处理如果用户换了一张图前端就会触发一次新的上传拿一个新的 OSS URL 覆盖旧值。回显本身不复杂初始化时给coverUrl赋值function initForm(record: { coverUrl: string }) { coverUrl.value record.coverUrl; }问题在于用户把原来的图删掉但 OSS 上的文件没删长期下来存储空间会越来越大。我在项目里用的是“前端只处理展示后端在保存时校验并清理旧文件”的策略。也就是提交表单时后端对比数据库里的旧 URL 和提交的新 URL如果不一样就定向删除数据库中旧 URL 对应的 OSS object。这个逻辑放在后端做前端不用操心。如果你没有后端配合自己纯前端实现至少要做到记录上传前后的 URL 差异在用户确定替换后调用一个受权限控制的后端接口去删除旧文件。直接在前端调 OSS 的 DeleteObject 接口不是不可以但需要服务端提供的临时凭证包含删除权限而给普通上传用的签名策略通常不会给删除权限这是刻意的上传场景权限最小化。5. 问题排查与避坑记录5.1 OSS CORS 配置导致的报错前端直传 OSS浏览器会发起跨域请求OSS 必须在服务端配置好 CORS 规则否则你会看到经典的Access to XMLHttpRequest at ... from origin ... has been blocked by CORS policy报错。这个问题的排查思路很简单打开浏览器 Network看那个发往 OSS 的请求是不是报 CORS error。如果是去阿里云 OSS 控制台对应 bucket 的“数据安全 跨域设置”里添加规则。来源 Origin 写你的前端域名允许的方法至少要勾选GET和POSTAllowed Header 填*暴露的 Header 里建议加上ETag方便后续做文件校验。有个容易忽略的小坑如果前端域名是http://localhost:5173那 CORS 规则里的来源要写http://localhost:5173不能图省事只写https://yourdomain.com否则本地开发环境直接白屏报错。另外很多人改了 CORS 规则后发现还是报错这是因为 CORS 规则下发有短时间延迟通常等一两分钟再试。不要疯狂刷新页面那不是阻碍。5.2 反复上传同一张图片却返回旧的 URL这个问题相当隐蔽同一张图片你改了内容重新上传OSS 返回的 URL 看起来却像旧文件。排查方向不是上传逻辑而是HTTP 缓存。浏览器或者 CDN 在本地缓存了这张图片URL 没变的情况下第二次打开就直接走了本地缓存不会真正请求 OSS。解决办法有几种上传时在 URL 后面追加一个版本参数${url}?v${Date.now()}文件名生成时加入随机部分比如我们已经用到的随机字符串从根部避免同名后端保存时用新的 object key保证 URL 唯一我项目里因为已经采用了时间戳 随机字符串命名基本避开了这个问题。但如果你用的是自定义固定文件名比如用户头像请务必在预览时加上时间戳参数。5.3 大文件上传中断、超时OSS 直传默认就是通过 XHR 发送完整文件浏览器方面没有统一的“超时”概念但网络波动、服务器拒绝长连接等导致上传中断的情况很常见。尤其是 10MB 以上的图片、几十 MB 的压缩包一旦中途断掉前端拿不到成功回调用户只能重传。针对大文件最正规的解决方案是 OSS 的分片上传Multipart Upload流程是先初始化分片上传拿到 UploadId然后一个个分片传最后合并。这个方案理论上有效但复杂度不小需要后端的策略配合还要考虑到分片之间的顺序问题。如果你只是偶尔传大文件可以先用一个折中方案上传使用xhr.upload.onprogress做实时进度展示同时在前端设置一个合理的超时时间比如 2 分钟无网络响应就提示用户。超过 2MB 的图片我建议在后端签名接口里把expiration时间调长一点避免 5 分钟过期大文件传到一半才发现签名失效那才是真正的心累。我通常把签名有效期设成 30 分钟。5.4 关于接口重复调用和 token 过期签名接口在某些场景下会被多次触发。比如用户连续选多张图每张图的上传都会调用一次getOssPolicy如果接口没有做缓存就会产生大量冗余请求。虽然不会出错但没必要。我在项目里做了个简单的策略缓存如果当前签名未过期直接复用。let cachedPolicy: OssPolicy | null null; let expireAt 0; async function getOssPolicyWithCache() { if (cachedPolicy Date.now() expireAt - 60 * 1000) { return cachedPolicy; } cachedPolicy await getOssPolicy(); expireAt (cachedPolicy.expire as number) * 1000; return cachedPolicy; }给expireAt预留 60 秒缓冲是因为网络传输本身消耗时间如果把过期时间卡得太死请求还没到 OSS签名就失效了。这个缓存看着简单实际能省掉不少无效请求也降低了签名接口的并发压力。6. 封装建议与开发环境注意事项如果你只是在一个页面上传单张图片按照上面的代码直接写问题不大。但如果项目里多个页面都要上传图片我强烈建议把这块抽成一个公共组件。组件的 API 设计上重点考虑几件事暴露modelValue或v-model:url让父组件直接绑定 URL支持传入accept、maxSize、multiple等配置上传中需要锁定按钮防止重复提交组件内维护uploading状态如果有进度条需求再传入onProgress回调一个常见的反面教材每写一个页面就把这段上传逻辑复制粘贴一遍。后面一旦要换 CDN 域名、加 HTTP 头、调签名接口路径你要改十几个文件绝对改到你怀疑人生。开发环境还有一个值得注意的点本地前端地址localhost:5173和生产环境域名会不一样OSS CORS 的规则要覆盖完整。很多团队是在配置里维护一份“允许的来源”列表然后同步到 OSS CORS 配置里本地、测试、生产各加一条省得环境切换时报错。如果你有统一网关或者说 Nginx 代理也可以考虑让本地通过代理转发到 OSS一定程度规避 CORS但这个方案更像变通不如 CORS 配置来得干净。7. 写在最后的经验如果你问我这个功能最大的坑在哪里我会说不是代码本身而是整个链路的边界不清晰。前端要管选文件、校验、进度、回显后端要管签名、鉴权、过期时间运维要管 CORS、Bucket 权限、CDN 刷新中间任何一个环节脱节体验就是报错或者图片打不开。建议在开发前就把上面这些内容理清楚尤其是签名接口的权限、CORS 的配置、文件名唯一性这三件事做得牢靠一点后面能省下很多沟通成本。就我个人这段时间实测下来这套方案在 Vue 3.3 Element Plus 2.4 版本上是稳定可用的el-upload 的核心 API 也很稳定没有出现因组件升级导致的大问题。如果哪天 Element Plus 调整了http-request的内置类型基本也就是改改类型声明的事上传逻辑本身完全不用动。
网站建设高端定制企业官网
RELATED

相关资讯

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

较早相关资讯

最新相关资讯

NestJS生产级限流实战:从装饰器到滑动窗口与可观测性 2026/10/2 11:00:03

NestJS生产级限流实战:从装饰器到滑动窗口与可观测性

1. 为什么 Nest 的限流不是加个装饰器就完事了?在 NestJS 生态里,“限流”这个词常被新手误读成一个“开箱即用”的功能开关——看到Throttle()装饰器,以为贴上就能防爆;看到nestjs/throttler包名,以为装完 npm instal…

阅读更多 →
DTFT核心性质全推导:从定义到卷积定理与Parseval定理 2026/10/2 11:00:03

DTFT核心性质全推导:从定义到卷积定理与Parseval定理

干了这么多年信号处理教学和工程实践,我一直有个很深的体会:很多人在考试或者面试前,会把“离散系统傅里叶变换”(DTFT)的常用结论背得滚瓜烂熟,但真被问到“这个结论是怎么来的”时,往往说不出…

阅读更多 →
基于低频FRF矩阵的刚性体惯性参数反演方法 2026/10/2 10:59:57

基于低频FRF矩阵的刚性体惯性参数反演方法

简介:本资源是一份面向机械工程领域研究人员与工程师的刚性体惯性参数识别技术实践指南,聚焦频响函数(FRF)驱动的参数辨识方法,解决复杂结构(如发动机、航天器部件)在多体动力学仿真中惯性参数难…

阅读更多 →
叠纸闪耀暖暖的3D换装工业化实践 2026/10/2 10:59:56

叠纸闪耀暖暖的3D换装工业化实践

1. 项目概述:当换装游戏不再只是“贴图”,而是一场三维建模的工业革命“叠纸”“闪耀暖暖”“2D到3D”——这三个词在2019年前后几乎同时引爆国内二次元与游戏美术圈。不是因为某张新皮肤海报,也不是因为某个联动活动,而是叠纸在一…

阅读更多 →
开源三天 9900 star!把 CodeX 塞进 Claude Code 后,TaoToken 统一 Key 怎么配? 2026/10/2 10:59:44

开源三天 9900 star!把 CodeX 塞进 Claude Code 后,TaoToken 统一 Key 怎么配?

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

阅读更多 →
Kimi Claw 实战:在 Kimi 里用 OpenClaw 的完整指南与 TaoToken 配置 2026/10/2 10:59:43

Kimi Claw 实战:在 Kimi 里用 OpenClaw 的完整指南与 TaoToken 配置

/* 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
📞 ✉