尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
链上存CID、链下存IPFS:Solidity+React去中心化网盘实战
简介这份去中心化文件存储平台项目源码面向具备 JavaScript 基础、希望实践区块链与分布式存储的开发者借助以太坊智能合约、Solidity、Web3.js 与 React.js 构建类 Dropbox 的文件上传、下载和权限管理流程并用 IPFS 承担实际文件存储解决传统中心化云存储在信任与单点依赖上的问题。压缩包共 26 个文件约 1.23MB以 js 前端逻辑与配置、sol 智能合约、json 依赖和合约描述、html/css 页面样式为主另含 png 图标、md 说明及环境变量示例覆盖合约迁移、组件开发与测试等环节。已有 335 人学习适合作为课程设计或毕业项目参考。读者可从中梳理合约编写、链上交互、IPFS 哈希存储与前端调用的完整链路并借鉴测试与目录组织方式快速理解去中心化文件系统的实现思路。1. 链上只放指针文件本体留在 IPFS一个 Dropbox 克隆的最小闭环第一次拆这类项目很多人会先踩一个认知坑以为“去中心化文件存储”就是把文件塞进区块链。真这么干一个 1KB 文件光 SSTORE 就要六十多万 gas成本高得没法用更像不现实的烧钱实验。这个项目走的是另一条路文件本体进 IPFS链上只留一个几十字节的 CID 字符串和一份权限表以太坊负责“谁拥有、谁能看”IPFS 负责“内容存在哪、怎么取”。整套东西用 Solidity 写合约、Web3.js 做链上适配、React.js 搭界面、JavaScript 串起前后端功能对标 Dropbox 的上传、列表、分享、撤销。适合想完整走一遍 DApp 全链路的开发者也适合手上有 React 基础、想弄明白“链上链下到底怎么分工”的人。2. 技术选型与整体架构四层分工把一次上传拆成几段先把边界划清楚后面写代码才不会乱。这个项目最值得学的地方不是某个函数写得多巧而是它把“存内容”和“存关系”彻底切开了。切开的依据是两边的成本结构和数据特性完全不同IPFS 擅长存大块、不可变的内容按哈希寻址以太坊擅长存小块的、需要鉴权和变更的状态。搞反了项目要么贵得跑不动要么权限形同虚设。2.1 为什么文件本体不能上链以太坊存储是按 32 字节一个槽位计费的把一个槽从零改成非零大约 20000 gas。换算下来1KB 数据差不多 64 万 gas一个 1MB 的图片就是 6 亿多 gas 量级。再加上区块 gas limit 通常在三千万上下单笔交易根本装不下稍大一点的文件。所以链上存文件本体这条路从经济模型上就走不通。IPFS 的思路完全不同。它把文件切成块每块算一个哈希最后得到一个内容标识 CID例如QmXoY...这样一串。同样的内容永远得到同样的 CID改一个字节 CID 全变。这就带来两个直接好处一是天然去重同一份文件被十个用户上传IPFS 里只存一份二是天然可校验拿到文件重算哈希对不上就是被篡改过。于是分工就很清楚了链上存cid fileName size owner timestamp这五个字段加起来不到 200 字节一次upload交易的 gas 控制在十万以内完全可接受文件本体躺在 IPFS 节点里前端拿到 CID 后拼接网关地址去取。这套设计里区块链其实退化成了一个“带签名的、不可篡改的目录服务”这才是它该干的活。提示CID 是内容哈希不是文件 ID。同一份文件重复上传会得到同一个 CID但合约里可以生成两条不同的记录——这一点在做去重逻辑时要特别注意。2.2 四层分工React、Web3.js、Solidity 与 IPFS 节点整个系统我习惯按四层去理解从上到下依次是界面层、适配层、合约层、存储层。每一层只干一件事层与层之间靠明确的输入输出对接任何一层换掉都不该影响其他层。层技术职责输入 → 输出界面层React.js选文件、展示进度、渲染文件列表用户操作 → 组件状态适配层Web3.js连钱包、拿账号、实例化合约、发交易方法调用 → 交易回执合约层Solidity存元信息、判权限、发事件交易 → 状态变更 event存储层IPFS 节点存文件块、按 CID 取回File → CID / CID → 字节流这张表看着简单落地时最常见的翻车点是“适配层越界”。新手容易在 React 组件里直接new Web3(...)、直接拼合约地址结果一换网络整个前端白屏。我的做法是把 Web3.js 的所有逻辑收进services/web3.js一个文件组件里只调用getContract()拿到的永远是当前链上已经部署好的合约实例。合约地址不硬编码从编译产物的networks[chainId]里读这样本地链、测试网、正式网三套环境用同一份代码。IPFS 那层也一样要收口。上传、取回、拼接网关 URL 全部封在services/ipfs.js组件只认uploadToIPFS(file)返回的字符串。哪天你把本地节点换成托管服务只改这一个文件。2.3 目录结构从 contracts 到 src 的职责边界仓库的目录就是上面四层分工的物理映射。我把一个典型结构列出来你拿到源码后可以对着看decentralized-drive/ ├── contracts/ │ └── FileStorage.sol # 唯一的核心合约 ├── migrations/ │ └── 2_deploy_storage.js # 部署脚本决定合约地址 ├── build/contracts/ │ └── FileStorage.json # 编译产物abi networks ├── src/ │ ├── services/ │ │ ├── ipfs.js # 上传/取回封装 │ │ └── web3.js # 连接、实例化、账号监听 │ ├── components/ │ │ ├── UploadBox.jsx # 选文件 进度条 │ │ ├── FileList.jsx # 我的文件 / 共享给我的 │ │ └── ShareDialog.jsx # 输入地址授权 │ ├── App.jsx │ └── index.js ├── public/ ├── truffle-config.js # 链配置端口、编译器版本 └── package.json几个容易忽略的点。build/contracts/FileStorage.json是前端和合约之间唯一的契约它同时包含abi怎么调和networks部署在哪条链的哪个地址改了合约一定要重新编译否则前端调的是旧 ABI报错会非常难查。migrations里的部署脚本别偷懒写成空文件它决定了合约地址怎么落到networks里。truffle-config.js里的compilers.solc.version要和合约头部的pragma solidity对得上版本不匹配是新手最常见的编译失败原因玄学程度堪比环境变量。React 那一侧App.jsx只需要管三件事初始化时调getContract()判断钱包是否就绪、监听accountsChanged和chainChanged两个事件、把account往下传。剩下所有业务逻辑都在组件和 services 里这样界面层薄改起来不牵一发动全身。3. 合约层拆解FileStorage 合约的存储结构与权限校验合约是整个项目的地基也是最该写死、最不该频繁改的部分。因为合约一旦部署就改不了权限判断、数据结构这些一旦设计错只能重新部署一份新合约再迁移数据。所以这一章我把设计取舍讲透你照着改的时候知道哪里能动、哪里不能动。3.1 存储结构三层 mapping 撑起“谁拥有、谁能看”合约里需要回答三个问题文件元信息存哪、某个人拥有哪些文件、某个人能不能看某个文件。对应三个 mapping一层套一层。// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract FileStorage { struct FileMeta { string cid; // IPFS 内容标识 string fileName; uint256 size; // 字节数前端展示用 address owner; uint256 uploadedAt; } // 文件 id 元信息 mapping(uint256 FileMeta) private files; // 用户地址 自己的文件 id 列表 mapping(address uint256[]) private ownedFiles; // 文件 id 用户地址 是否有读权限 mapping(uint256 mapping(address bool)) private access; uint256 public fileCount; // 自增 id从 1 开始 }files存主体数据ownedFiles是为“我的文件”列表准备的索引access是权限矩阵。为什么要单独维护ownedFiles因为 Solidity 里没法遍历 mapping你不可能靠扫files找出某人的所有文件必须在上传时顺手往数组里 push 一条。这是链上开发跟传统后端最不一样的地方——所有“查询”都得提前建好索引否则只能靠事件在链下重建。access用了嵌套 mapping写成access[id][addr]读的时候一次哈希定位gas 是常数级。如果把权限做成一堆address[]数组再遍历文件一多 gas 就爆了。这个坑我在别的项目里踩过一个共享列表循环到第 200 个地址时交易直接超 gas 失败血泪经验。3.2 四个核心方法upload、grantAccess、getFile、listMyFiles方法不多就四个但每一个都藏着细节。event FileUploaded(uint256 indexed id, address indexed owner, string cid, string fileName); event AccessGranted(uint256 indexed id, address indexed to); event AccessRevoked(uint256 indexed id, address indexed from); function upload( string calldata cid, string calldata fileName, uint256 size ) external returns (uint256 id) { require(bytes(cid).length 0, empty cid); require(size 0, empty file); id fileCount; // 从 1 开始0 留作无效值 files[id] FileMeta(cid, fileName, size, msg.sender, block.timestamp); ownedFiles[msg.sender].push(id); access[id][msg.sender] true; // 上传者默认有权限 emit FileUploaded(id, msg.sender, cid, fileName); } function grantAccess(uint256 id, address to) external { require(files[id].owner msg.sender, not owner); require(to ! address(0), zero address); access[id][to] true; emit AccessGranted(id, to); } function revokeAccess(uint256 id, address from) external { require(files[id].owner msg.sender, not owner); access[id][from] false; emit AccessRevoked(id, from); } function getFile(uint256 id) external view returns (FileMeta memory) { require(access[id][msg.sender], no permission); return files[id]; } function listMyFiles() external view returns (uint256[] memory) { return ownedFiles[msg.sender]; } }逐段说。upload里先做两个requirecid不能为空是因为空 CID 对应的文件根本取不回来前端一旦传错就是脏数据上链永远删不掉size 0同理。id fileCount用前置自增保证第一个文件 id 是 1这样id 0可以安全地当作“不存在”判断省掉一个存在性 mapping。grantAccess的权限判断写成files[id].owner msg.sender只有拥有者能授权这是整个权限模型的核心。注意这里没有引入Ownable或者管理员角色因为文件级的所有权比合约级的所有权更细一个管理员能看所有人的文件那就不叫去中心化存储了。getFile是view方法不花 gas但里面带require(access[id][msg.sender])意味着调用方必须是授权用户。这里有个隐含前提msg.sender在view调用里由节点根据from参数填前端call({ from: account })必须显式传不传的话默认零地址一定报no permission。listMyFiles直接返回数组文件多了会有点费 RPC一般几百条以内没问题上千条就得改成带分页的listMyFiles(uint256 offset, uint256 limit)用for循环切片返回。注意revokeAccess不能撤销 owner 自己的权限否则会出现“文件还在但谁也看不了”的死锁。如果要更严谨加一行require(from ! files[id].owner)。3.3 事件驱动的列表刷新为什么要靠 event 而不是轮询传统后端上传完刷新列表一个GET /files就完事。链上没有这种接口你有两个选择轮询listMyFiles()或者监听事件。轮询的问题是延迟不可控。交易发出后要等打包本地链一两秒测试网可能十几秒你只能每隔两秒调一次call用户体验很差而且 RPC 请求量上去了容易被限流。事件就没这问题交易一上链FileUploaded立刻触发前端订阅到之后主动刷新。// services/web3.js 里补一段订阅 export function subscribeUpload(contract, onChange) { const sub contract.events.FileUploaded({ fromBlock: latest }) .on(data, (ev) { // ev.returnValues 里就是 emit 时带出去的字段 onChange({ id: ev.returnValues.id, cid: ev.returnValues.cid, fileName: ev.returnValues.fileName, owner: ev.returnValues.owner, }); }); return () sub.unsubscribe(); // 组件卸载时必须解绑 }fromBlock: latest是关键参数只订阅新块不去扫历史否则第一次连上就要把整条链扫一遍浏览器直接卡死。历史数据靠初始化时调一次listMyFiles()补齐事件只负责增量——这是链上应用的标准套路。indexed关键字也值得说一句。事件参数加了indexed就能按它过滤比如FileUploaded({ filter: { owner: account } })。但indexed最多三个加上id已经用掉一个字符串类的cid就别加了加了也只能存哈希过滤时反而对不上。3.4 本地起链与合约部署三步把合约送上链合约写完跑起来分三步起本地链、改配置、部署。# 1. 起一条本地开发链默认监听 8545预置 10 个带余额的账号 ganache --port 8545 --chain.chainId 1337 # 2. 编译合约产物落到 build/contracts/ npx truffle compile # 3. 部署配置里 development 指向 8545 npx truffle migrate --network development --reset--chainId 1337建议显式指定很多钱包扩展会把 1337 当作默认本地链id 对不上就会出现“合约没部署到当前网络”的报错排查起来很费时间。--reset是重新部署的意思改了合约一定要加否则 truffle 会认为迁移文件没变而跳过。部署完成后打开build/contracts/FileStorage.json找到networks字段应该能看到类似这样的内容{ networks: { 1337: { address: 0x5FbDB2315678afecb367f032d93F642f64180aa3 } } }这个address就是前端要用的合约地址。如果你发现networks是空的说明 migrate 根本没跑成功先去看终端报错别急着改前端。4. 前端与 IPFS 链路从选择文件到链上确认的完整代码前端这一块真正需要自己写的只有三个文件IPFS 封装、Web3 封装、上传组件。剩下的列表渲染、分享弹窗都是常规 React 活。我按一次点击的数据流顺序讲。4.1 上传封装把 File 变成 CID浏览器的File对象不能直接扔给 IPFS得走 HTTP API 或者 JS 客户端。项目里用的是 HTTP 客户端指向本地节点的 5001 端口。// src/services/ipfs.js import { create } from ipfs-http-client; // 本地节点默认 API 端口 5001换成远程节点只改这一行 const client create({ url: http://127.0.0.1:5001/api/v0 }); export async function uploadToIPFS(file, onProgress) { const { cid } await client.add( { path: file.name, content: file }, // 带 path 能保留文件名 { pin: true, // 关键钉住节点重启不丢 progress: (bytes) { onProgress?.(Math.min(100, Math.round((bytes / file.size) * 100))); }, } ); return cid.toString(); // 返回 Qm... 字符串 }pin: true是这一段里最重要的参数。IPFS 节点默认只缓存被访问过的块垃圾回收一跑就没了你会遇到“链上明明有 CID网关却 404”的诡异现象。加上 pin节点会把它当长期数据保留。path: file.name让 CID 里带上文件名信息取回时能拿到原始名字不加的话只是纯字节流。progress回调给的是已上传字节数不是百分比得自己除。大文件上传时这个回调触发很频繁别在里面直接setState用节流包一层否则 React 会疯狂重渲染。4.2 Web3.js 连接钱包与合约实例化这一步是整个前端最容易出兼容问题的地方我把它写成纯函数任何组件调用都拿到同样的结果。// src/services/web3.js import Web3 from web3; import artifact from ../../build/contracts/FileStorage.json; export async function getContract() { if (!window.ethereum) { throw new Error(未检测到钱包扩展请先安装并创建账户); } // 请求授权用户拒绝会抛错必须 try/catch await window.ethereum.request({ method: eth_requestAccounts }); const web3 new Web3(window.ethereum); const accounts await web3.eth.getAccounts(); const chainId await web3.eth.getChainId(); const deployed artifact.networks[chainId]; if (!deployed) { throw new Error(合约未部署到 chainId${chainId}请检查网络); } const contract new web3.eth.Contract(artifact.abi, deployed.address); return { web3, contract, account: accounts[0], chainId }; }web3.eth.getChainId()返回的是数字而artifact.networks的 key 是字符串JS 对象取值时会自动转看着像能用但如果你用比较就会翻车。这是我在两个项目里都栽过的细节。eth_requestAccounts必须在用户手势点击里调用页面加载时自动弹窗会被浏览器拦截控制台只给你一句含糊的警告。4.3 上传组件的完整交互两步走缺一不可组件里要做的事其实就两件但顺序不能反先传 IPFS 拿 CID再写合约。// src/components/UploadBox.jsx import { useState } from react; import { uploadToIPFS } from ../services/ipfs; import { getContract } from ../services/web3; export default function UploadBox({ onDone }) { const [progress, setProgress] useState(0); const [busy, setBusy] useState(false); async function handleFile(e) { const file e.target.files?.[0]; if (!file) return; setBusy(true); try { // 第一步文件进 IPFS这一步不花 gas但可能很慢 const cid await uploadToIPFS(file, setProgress); // 第二步CID 写进合约这一步要签名、要等打包 const { contract, account } await getContract(); await contract.methods .upload(cid, file.name, file.size) .send({ from: account, gas: 300000 }); setProgress(100); onDone?.(); } catch (err) { console.error(上传失败, err.message); } finally { setBusy(false); e.target.value ; // 清空 input否则选同一个文件不触发 change } } return ( div input typefile onChange{handleFile} disabled{busy} / {busy progress value{progress} max100 /} /div ); }顺序不能反的原因很实在合约一旦写入 CID 就不可改如果先写合约再传 IPFS 且 IPFS 失败链上就留下一个永远取不回来的死记录还白花了 gas。所以永远是“先落地内容再登记指针”。gas: 300000是手动设的。upload方法实际消耗大概 15 万到 20 万 gas取决于文件名字符串长度留 30 万是余量。不手动设的话 Web3.js 会走estimateGas本地链上偶尔估不准报cannot estimate gas的错。宁可多留。e.target.value 这一行别省。不清空的话用户选同一个文件第二次不会触发onChange界面像卡死了一样新手很容易以为是 IPFS 的问题实际是 DOM 的锅。4.4 联调清单从 npm install 到看到第一条记录前端跑起来之前对着这个清单过一遍能省掉八成“打不开”的问题。# 1. 装依赖 npm install # 2. 确认本地链在跑另开一个终端别关 ganache --port 8545 --chain.chainId 1337 # 3. 确认 IPFS 节点在跑 ipfs daemon # 4. 合约已部署build/contracts/FileStorage.json 里 networks 非空 # 5. 起前端 npm start启动后依次验证四件事钱包能连上、账号地址显示正确、选一个小文件上传能看到进度条走完、刷新页面后列表里还有这条记录。第四步是关键如果刷新就没了说明你的列表是从组件 state 读的而不是从链上拉的得回去改useEffect里的初始化逻辑。提示上传测试用几十 KB 的小文件就行别一上来丢个几百 MB 的视频那样出了问题你分不清是 IPFS 慢、gas 不够还是浏览器内存爆了。5. 避坑与常见问题排查五个真会卡住你的点下面这几条基本都是我自己或者身边人实打实卡过半天以上的。按「现象 → 原因 → 解决」记下来遇到时直接对号入座。现象一合约里有 CID浏览器打开网关却 404。原因多半是 IPFS 节点把数据回收了。默认配置下节点只缓存定期 GC 会把没被 pin 的块清掉尤其你反复重启ipfs daemon的时候。解决方式是上传时带pin: true已经丢的用ipfs pin add cid补回来如果节点本身就是临时起的那数据本来就没落盘得重新上传。现象二交易报cannot estimate gas或者一直 pending 不返回。前者的原因通常是from地址没传、或者合约地址指向了一个没有代码的空地址——new web3.eth.Contract(abi, wrongAddress)不会报错只有真正发交易时才炸。后者多半是本地链重启过nonce 对不上在钱包里点“重置账户”或者干脆换个账号。排查顺序是先web3.eth.getCode(address)看是不是0x再确认 chainId 和 networks 的 key 一致。现象三换了网络前端整个白屏控制台报Cannot read properties of undefined。原因是合约地址被硬编码在代码里或者从networks里取值时没做存在性判断直接访问.address。解决是统一走getContract()取不到就抛一句人能看懂的错而不是让 undefined 一路传下去。这件事我坚持在每一个链上项目里做代价是几十行代码收益是排查时间从半天缩到五分钟。现象四上传大文件浏览器卡死或者内存飙到几个 G。原因是client.add默认把整个文件读进内存再切块。几十 MB 以内还好上百 MB 就开始明显卡顿。解决是分片用file.slice(start, end)切成 5MB 左右的小块分别add最后用一个目录结构串起来或者干脆改成流式读取。另一个思路是前后端分离大文件走单独的 Node 服务中转浏览器只负责拿 CID。现象五在钱包里切换账号文件列表还是上一个人的。原因是只监听了组件挂载时的account没订阅accountsChanged事件。解决是在App.jsx里加监听账号一变就重新getContract()并重新拉列表。顺带一提chainChanged也要监听用户从本地链切到别的网络时合约地址就变了不重新实例化必然报错。这两个监听是链上前端的基本功漏一个就会有用户来问你“为什么我的文件不见了”。6. 二次开发把这个 Dropbox 克隆改成你自己的业务网盘原项目的骨架是干净的但离“能交付”还差几步。分享功能只做了地址授权没有做“分享链接”文件列表没有分页文件一多就卡最要命的是文件明文躺在 IPFS 上任何拿到 CID 的人都能通过公共网关打开。真要拿去用我一般按下面三个方向改按投入产出排序。改造方向改动位置工作量验证方法文件加密后再上 IPFSservices/ipfs.js前加一层小用别的网关打开 CID应该是乱码团队空间 / 目录分组合约加groupId字段 前端分组中两个账号互不可见同组可见版本历史合约改成mapping(uint256 string[])存 CID 数组中同名文件上传两次能回滚到上一版先说加密性价比最高。思路很简单文件在浏览器里用 AES-GCM 加密密文上 IPFS密钥不上链通过合约授权之外的通道比如钱包签名派生传给被分享的人。// src/services/crypto.js —— 上 IPFS 之前先加密 export async function encryptFile(file, rawKey) { const key await crypto.subtle.importKey( raw, rawKey, { name: AES-GCM }, false, [encrypt] ); const iv crypto.getRandomValues(new Uint8Array(12)); // 每个文件独立 IV const cipher await crypto.subtle.encrypt( { name: AES-GCM, iv }, key, await file.arrayBuffer() ); // iv 拼在密文前面解密时前 12 字节切出来 return new Blob([iv, cipher]); }这里iv必须每个文件都不一样复用 IV 在 GCM 模式下会直接泄漏密钥这是密码学里的硬规矩不是可选项。密钥的传递是这套方案里最麻烦的部分简单做法是分享时把密钥放在链接的 fragment 里#后面不会被发到服务器复杂做法是用对方的公钥做一次 ECDH 协商。前者够用后者更正经。再说团队空间。合约里加一个mapping(uint256 uint256) fileGroup和一个mapping(uint256 mapping(address bool)) groupMembergetFile的权限判断从“我是 owner 或被单独授权”改成“我是 owner、被单独授权、或我在这个组里”。改动不大但要注意listMyFiles得跟着改否则用户看不到别人共享到组里的文件。验证方式很直接开两个浏览器窗口两个账号A 建组拉 B 进来A 上传B 刷新能看到C 看不到就算通过。最后是版本历史。把FileMeta.cid从string改成string[]每次同文件名上传就push一条getFile返回最新一版再加一个getFileVersion(id, index)。这样改的好处是旧版本的内容在 IPFS 里天然还在内容寻址不会覆盖坏处是 gas 会随版本数线性涨。所以我会加一个上限比如保留最近 10 版超出的不删链上删不掉只是不再返回。改造完一定要做一遍回归拿一个新账号从零走一遍上传、分享、撤销、刷新四条路径都通了才算完。从那以后我每次接这类链上存储的项目都强制自己先在 README 第一行写清楚“链上存什么、链下存什么、谁负责鉴权”三句话写不出来就先别写代码——因为这个边界一旦模糊后面每一行代码都在给未来的自己挖坑。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

吉他入门科学路径:从振动原理到30天弹唱实战

吉他入门科学路径:从振动原理到30天弹唱实战

1. 这不是“随便拨两下弦”的入门,而是真正能让你三个月后弹出完整歌曲的系统起点“从零开始认识吉他”这八个字,最近在几个主流学习平台的搜索量翻了三倍。但点开那些标题党内容,十有八九是“5分钟学会C和弦”“3天弹唱《晴天》”&#xff0…

📅 2026/10/29 23:39:33
Caveman静态站点生成器:极简博客构建与部署指南

Caveman静态站点生成器:极简博客构建与部署指南

说实话,第一次看到“caveman”这个项目名的时候,我愣了几秒。程序员圈子里给项目起名的路子通常要么是高大上的希腊神话,要么是极客风的名词缩写,突然冒出来一个“穴居人”,反而让人觉得有点意思。等我把它跑起来之后才…

📅 2026/10/29 23:39:33
技能管理实战:从盘点、刻意练习到变现的完整框架

技能管理实战:从盘点、刻意练习到变现的完整框架

很多人觉得自己“什么都会一点,但什么都不精”,或者反过来,觉得自己“一直在学,但学了就忘”。这种状态我太熟悉了,我自己经历过,也在带团队、做咨询时无数次看到过。问题不在智商,也不在毅力&a…

📅 2026/10/29 23:34:32
MORE NEWS

更多资讯

📰

纯视觉自动驾驶的光影挑战与解决方案

1. 纯视觉自动驾驶的光影困局解析当特斯拉的摄像头在夕阳下突然"失明",或是Waymo的传感器被对面车灯晃得不知所措时,我们不禁要问:号称要颠覆出行方式的纯视觉自动驾驶,为何总在基础的光影变化面前败下阵来?…

📰

具身智能机器人从入门到产业落地:ROS2、仿真与真机实战要点

最近身边好几个朋友在讨论同一个话题:具身智能机器人到底怎么学、怎么切入产业。有人一上来就抱着《ROS2机器人开发从入门到实践》啃了一个月,结果连一个机械臂关节都转不利索;也有人花大几千报了培训班,最后只在仿真环境里拖了几…

📰

从技能管理到复盘闭环:一套可落地的个人技能成长系统

这些年我在技能这件事上栽过不少跟头。光看到"skills"这个词,你可能觉得它就是个常见英文单词,跟"技能"划等号就完了。但在我这里,它代表了一套折腾了快两年、迭代了四个版本的个人技能管理系统。起因特别朴素&#xff1…

📰

SSM微博系统毕设资源包:从建库到答辩的完整跑通指南

简介:这是一套面向高校计算机专业学生与Java初学者、可用于毕业设计或课程实践的微博管理系统完整资料包,基于JavaEE、SSM框架与MySQL数据库,采用B/S架构实现。系统涵盖用户登录注册与信息维护、微博发布修改及点赞转发收藏评论、好友关注与私…

📰

导线网平差程序实现指南:间接平差与Python编程避坑要点

简介:导线网平差程序是一套面向武汉大学测绘学院平差课程设计的C# WPF实现,完整覆盖导线网近似值计算、角度与边长观测值处理、平差值迭代、坐标改正数求解,以及验后单位权中误差、坐标参数方差阵、点位中误差和边角观测值改正数等精度评定指…

📰

0.32 秒延迟之外:把说话人分离与 30B 转写拼成一条流水线

0.32 秒延迟之外:把说话人分离与 30B 转写拼成一条流水线 【免费下载链接】NVIDIA-Nemotron-3.5-Transcribe-30B-A4B 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/NVIDIA-Nemotron-3.5-Transcribe-30B-A4B 「0.32 秒」这个数字在最近两周的社区文章…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬