[{"data":1,"prerenderedAt":101},["ShallowReactive",2],{"case-dev-build-log-android-file-transfer-assistant-zh":3,"blog-list-zh":13},{"slug":4,"title":5,"summary":6,"date":7,"featured":8,"seoDescription":9,"series":10,"seriesOrder":11,"html":12},"dev-build-log-android-file-transfer-assistant","测试机连上以后，我们又做了个文件互传助手","团队测试机通常没有聊天软件，也不会登录个人账号，传一个 URL、截图或安装包都很麻烦。Android 配套 App 做完后，我们顺手在已有配对链路旁加了一条局域网互传通道。","2026-09-14",false,"DevPeek 开发实录：团队测试机没有聊天软件时，如何通过 Android 配套 App 的独立 WebSocket 与 HTTP 局域网通道，在手机和电脑之间互传文字、图片、视频和文件。","dev-build-log",6,"\u003Ch2>「把这个链接发到测试机上」\u003C/h2>\n\u003Cp>Android 配套 App 做出来以后，测试机已经可以扫配对码，把流量切到电脑上的 DevPeek。接口调到一半，我们又碰到另一件很小、但一天会发生很多次的事：\u003C/p>\n\u003Cblockquote>\n\u003Cp>「把这个链接发到测试机上，我用真机打开。」\u003C/p>\n\u003C/blockquote>\n\u003Cp>自己的手机上，这句话通常意味着发到「文件传输助手」或者自己的聊天窗口，再从手机点开。但团队测试机一般不会装这些聊天、通讯软件，也不应该登录某位同事的个人账号。\u003C/p>\n\u003Cp>链接短一点还能手敲。遇到带一长串 Query 的活动页、临时环境地址，输一次就很容易漏字符。临时生成二维码、打开一个局域网页面、接数据线跑 \u003Ccode>adb push\u003C/code>，都能解决，但为了传几行文字，每次都要先搭一座桥。\u003C/p>\n\u003Cp>反方向更麻烦：测试机刚截的异常页面要传回电脑，手机里的日志和 JSON 要拿到桌面看，录屏要交给开发复现。没有通讯软件时，往往又得插线、开临时文件服务，或者先想办法把文件传到另一台手机。\u003C/p>\n\u003Cp>\u003Ca href=\"/blog/dev-build-log-android-apk/\">上一篇\u003C/a> 解决的是测试机换人时反复改系统代理。做到配对之后，一个很自然的问题就冒出来了：\u003Cstrong>手机和电脑既然已经知道彼此，能不能顺手把这些信息也直接传过去？\u003C/strong>\u003C/p>\n\u003Ch2>我们想做的不是另一个聊天软件\u003C/h2>\n\u003Cp>互传助手不是 Android App 最初的目标。扫码切代理这条主链做完以后，手机与电脑已经有了配对关系、设备名和可达地址，我们只是顺着这些现成条件，多解决一个很窄的场景：当前测试机与当前配对电脑之间，临时交换联调资料。\u003C/p>\n\u003Cp>电脑上的 DevPeek「手机」面板和 Android App 里都有一个对话式入口。文字可以双向发送；图片、视频和普通文件也可以从任意一端选取。收到内容后：\u003C/p>\n\u003Cul>\n\u003Cli>文字可长按复制，适合 URL、JSON 片段和临时参数；\u003C/li>\n\u003Cli>图片可以直接预览，也可以复制后粘到其它应用；\u003C/li>\n\u003Cli>视频可以在会话里打开播放，不必先另存再找播放器；\u003C/li>\n\u003Cli>APK、HAR、日志等普通文件显示文件名、大小和传输进度，完成后可另存；\u003C/li>\n\u003Cli>传输失败可以重试，断开后仍能查看之前的会话记录。\u003C/li>\n\u003C/ul>\n\u003Cp>单文件当前限制为 \u003Cstrong>2 GB\u003C/strong>。这是临时互传，不做文件夹同步、云端备份，也不自动同步整块剪贴板。APK 在这里也只是一个普通文件，传到手机后仍要按 Android 的安装流程确认，不是远程安装器。\u003C/p>\n\u003Cp>界面做成聊天样式，并不是想继续长成聊天工具，只是「谁发的、发了什么、传完没有」用左右气泡最容易看清。测试机在几台电脑之间切换时，会话也按电脑分开，不会把上一位同事发来的内容混进当前窗口。\u003C/p>\n\u003Ch2>互传不能走抓包端口\u003C/h2>\n\u003Cp>第一反应可能是：手机本来就连着 DevPeek 的 HTTP 代理，文件也从 8888 传不就行了？\u003C/p>\n\u003Cp>真这么做，会把两件事搅在一起。\u003C/p>\n\u003Cp>8888 是 MITM 抓包链路。目标 App 的 HTTP(S) 经过这里，才会进入列表、SSL 解密、Mock 和 \u003Ca href=\"/docs/param-transform/\">参数转换\u003C/a>。互传的截图或安装包如果也从这里经过，大文件会被当成普通抓包请求处理，既污染列表，又给代理引擎增加一份完全没必要的正文。\u003C/p>\n\u003Cp>还有更麻烦的一层：Android 配套 App 自己就在 VpnService 里声明了 HTTP 代理。如果互传请求继续遵循这份系统代理，它会先被送到 8888，再尝试访问电脑上的互传服务。路径绕回自己建立的 VPN，稍不注意就会形成环路。\u003C/p>\n\u003Cp>所以互传没有复用抓包端口，而是在旁边开了一条 \u003Cstrong>Assist 通道\u003C/strong>：\u003C/p>\n\u003Cul>\n\u003Cli>抓包代理继续监听自己的端口，默认是 8888；\u003C/li>\n\u003Cli>Assist 服务优先使用 17891，端口被占用时自动寻找下一个可用端口；\u003C/li>\n\u003Cli>实际 Assist 端口跟着配对信息发给手机，用户不用再填一次；\u003C/li>\n\u003Cli>Assist 连不上，不影响 VPN 抓包；两条链路各自失败、各自恢复。\u003C/li>\n\u003C/ul>\n\u003Cp>二进制文件不会进入抓包引擎，也不会出现在 \u003Ca href=\"/docs/capture/\">抓包列表\u003C/a> 里。互传是配对后的附加能力，不是一次「自己抓自己」的 HTTP 请求。\u003C/p>\n\u003Ch2>手机必须绕回真实 Wi-Fi\u003C/h2>\n\u003Cp>端口分开还不够。Android 上的互传连接还要明确告诉系统：\u003Cstrong>这条 Socket 不走当前 VPN。\u003C/strong>\u003C/p>\n\u003Cp>App 建立 Assist 客户端时做了两层处理：\u003C/p>\n\u003Col>\n\u003Cli>强制 \u003Ccode>NO_PROXY\u003C/code>，不读取 VpnService 注入的 HTTP 代理；\u003C/li>\n\u003Cli>用 \u003Ccode>UnderlyingNetwork.bind()\u003C/code> 把 Socket 绑定到手机当前的真实网络，通常就是扫码时使用的 Wi-Fi。\u003C/li>\n\u003C/ol>\n\u003Cp>最终路径不是：\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-text\">手机 → VpnService → 8888 抓包代理 → Assist\n\u003C/code>\u003C/pre>\n\u003Cp>而是：\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-text\">手机 → 真实 Wi-Fi → 电脑 Assist 端口\n\u003C/code>\u003C/pre>\n\u003Cp>这条路径避免了 TUN 环路，也让互传文件不进入抓包列表。手机仍然可以同时把浏览器流量送到 DevPeek：一个 App 里有两条连接，一条为目标流量服务，一条为手机与电脑之间的信息交换服务。\u003C/p>\n\u003Cp>这也是为什么互传要求手机和电脑处于可直接访问的局域网。它不经过云端服务器，也不会为了发一张截图先上传公网再下载回来。\u003C/p>\n\u003Ch2>消息走 WebSocket，文件走 HTTP\u003C/h2>\n\u003Cp>文字和文件不适合硬塞进同一种消息。\u003C/p>\n\u003Cp>互传建立后，手机先通过 WebSocket 向桌面发送 \u003Ccode>hello\u003C/code>，带上设备 ID、机型和平台。桌面回一条 \u003Ccode>hello.ok\u003C/code>，双方才把这台设备标为可互传。之后，文字以及文件状态都走这条长连接：\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-text\">text\nfile.offer\nfile.progress\nfile.done\nfile.fail\n\u003C/code>\u003C/pre>\n\u003Cp>文件本体则走 HTTP。发送方先用 \u003Ccode>file.offer\u003C/code> 告诉对方文件名、大小和 MIME 类型，再向 \u003Ccode>/assist/files/{id}\u003C/code> 上传；接收方拿到完成消息后，从同一路径下载。这样 WebSocket 只承担轻量信令，大文件可以流式读写、显示进度，也能单独校验和保存。\u003C/p>\n\u003Cp>连接断开时，正在传的项目会标记为失败，不会一直停在「传送中」。Android 端随后按退避间隔尝试恢复 Assist 连接；恢复不了，也不影响已经建立的抓包 VPN。历史消息和已接收文件会留在本机，下次打开还能查看。\u003C/p>\n\u003Cp>我们没有为了「协议统一」把几百 MB 的视频编码进 JSON，也没有让文件传输反过来绑住代理核心。工具内部多一条通道，换来的是抓包和互传互不拖累。\u003C/p>\n\u003Ch2>本地直传，也不等于可以忽略安全边界\u003C/h2>\n\u003Cp>不要求测试机安装聊天软件、登录个人账号，也意味着临时资料不用先上传到第三方服务；但「局域网直传」不自动等于「端到端加密」。\u003C/p>\n\u003Cp>当前 Assist 使用局域网内的 \u003Ccode>ws://\u003C/code> 与 \u003Ccode>http://\u003C/code>，不提供公网中继，也没有面向不可信网络设计的账号认证和端到端加密。它遵循的仍是配对场景：手机与电脑在同一个可信局域网，配对码不要发到公网，Assist 端口也不要暴露到公共网络。\u003C/p>\n\u003Cp>传 Token、Cookie、生产数据或用户隐私之前，仍要先判断这些内容是否应该离开原设备。需要长期保存、权限控制或审计的文件，应该回到团队正式的协作与存储系统。DevPeek 的互传助手解决的是眼前这一次搬运，不替代这些系统。\u003C/p>\n\u003Cp>它与桌面的 \u003Ca href=\"/docs/collaboration/\">局域网协作\u003C/a> 也有区别：局域网协作面向两台 DevPeek，发送抓包请求和调试录制；互传助手面向 Android 测试机与当前电脑，发送文字和普通文件。\u003C/p>\n\u003Ch2>配对之后，少绕一个中转站\u003C/h2>\n\u003Cp>我们做 Android 配套 App，最初只是为了让测试机换人时不用反复修改系统代理。互传助手是这条主链完成后捎带实现的：既然配对信息和局域网连接已经有了，就再少折腾一次数据线、临时二维码或文件服务。\u003C/p>\n\u003Cp>以前，把 URL 放到没有通讯软件的测试机上，要手敲、扫临时二维码或者插线；把截图拿回电脑，又要反着走一遍。现在测试机既然已经和桌面建立了关系，就让文字和文件沿着这段局域网连接直接过去。\u003C/p>\n\u003Cp>不要求云盘，不要求联系人，也不要求在公共测试机上登录谁的个人账号。只是从电脑到手机，再从手机回来。\u003C/p>\n\u003Ch2>相关文档\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/docs/quick-start/\">快速上手\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/docs/capture/\">抓包与过滤\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/docs/param-transform/\">参数转换\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/docs/collaboration/\">局域网协作\u003C/a>\u003C/li>\n\u003Cli>\u003Ca href=\"/docs/faq/\">常见问题\u003C/a>\u003C/li>\n\u003C/ul>\n\u003Chr>\n\u003Cp>配套 Android App 与互传助手仍在随桌面 1.4.x 打磨，目前不上架。电脑侧可从 \u003Ca href=\"/\">DevPeek 官网\u003C/a> 下载；如果你经常在手机和电脑之间搬 URL、截图、日志或安装包，欢迎到 \u003Ca href=\"https://github.com/GYPengDev/devpeek/discussions\">GitHub Discussions\u003C/a> 说说最常传的是什么。\u003C/p>\n",{"items":14},[15,21,22,29,36,42,48,55,62,68,74,82,88,95],{"slug":16,"title":17,"summary":18,"date":7,"featured":8,"seoDescription":19,"series":10,"seriesOrder":20},"dev-build-log-android-apk","测试机借来借去，我们不想再改系统代理","测试机在团队里转一次，Wi-Fi 代理就要跟着改一次。普通 App 写不了系统代理，于是我们做了 Android 配套 App，用 VpnService 把「换 IP、换端口」收成扫码切换。","DevPeek 开发实录：为什么做 Android 配套 App。测试机在团队里流转时不再反复修改 Wi-Fi 代理，而是通过 VpnService 扫码切换当前抓包电脑。",5,{"slug":4,"title":5,"summary":6,"date":7,"featured":8,"seoDescription":9,"series":10,"seriesOrder":11},{"slug":23,"title":24,"summary":25,"date":7,"featured":8,"seoDescription":26,"series":27,"seriesOrder":28},"https-origin-cert-mismatch","源站证书与域名不匹配？DevPeek 为什么仍能正常抓包","DevPeek 检测到源站证书与 Host 不匹配时，不会静默忽略，也不会直接中断调试；网站和 HTTPS 明文仍可查看，同时会在请求详情中标记证书异常。","遇到 ERR_TLS_CERT_ALTNAME_INVALID 时，DevPeek 为什么仍能打开网站并查看 HTTPS 明文？本文说明源站证书、Host 与 SAN 的关系，以及 DevPeek 如何继续解密并明确报告证书风险。","api-debug-new-tricks",4,{"slug":30,"title":31,"summary":32,"date":33,"featured":8,"seoDescription":34,"series":27,"seriesOrder":35},"https-ssl-skip-decrypt","开了 SSL 抓包，有些网站反而打不开？把域名加进「跳过 SSL 代理」","系统代理和证书都已配置好，但个别网站或 App 一做 HTTPS 解密就无法访问。把对应 Host 加入「跳过 SSL 代理」，只让这部分流量走 CONNECT 隧道，不必关闭全部 HTTPS 抓包。","2026-09-01","DevPeek 开启 SSL 代理后个别网站或 App 无法访问？如果关闭解密或改走 CONNECT 隧道后恢复，可用 Include / Exclude 只跳过不兼容的 Host，保留其它 HTTPS 明文抓包。",3,{"slug":37,"title":38,"summary":39,"date":40,"featured":8,"seoDescription":41,"series":10,"seriesOrder":28},"dev-build-log-silent-auto-update","换掉 Electron 后，自动更新也得重做","DevPeek 换成 Tauri 后，更新的不只是一个窗口，而是 Launcher、Core 和 Shell 三个进程。我们把检查和下载挪到后台，等安装包准备好后，再由托盘完成最后一次重启。","2026-08-28","DevPeek 开发实录：桌面端从 Electron 换到 Tauri 后，如何为 Launcher、Core 和 Shell 重做自动更新，并避免更新过程影响系统代理。",{"slug":43,"title":44,"summary":45,"date":46,"featured":8,"seoDescription":47,"series":10,"seriesOrder":35},"dev-build-log-ws-mock-dsl","为什么我们重新设计了 WebSocket Mock DSL，而不是用 YAML、JSON 或 JS","轻量 WebSocket Mock 不该比 HTTP Mock 更重。YAML、JSON、JS 都能做，但写「连上、登录、心跳」太啰嗦。我们自研短 DSL，就是让轻量 Mock 更轻。","2026-08-13","DevPeek 开发实录：为什么 WebSocket Mock 自研 Flow DSL。现成 YAML、JSON、JS 能做重活；轻量顺序剧本用更短的文本来写。",{"slug":49,"title":50,"summary":51,"date":52,"featured":8,"seoDescription":53,"series":10,"seriesOrder":54},"dev-build-log-electron-to-tauri","DevPeek 架构改造：抛弃 Electron，拥抱 Tauri","联调工具不该为「开一个窗口」再拖一整套 Chromium。我们把业务收到 Core，桌面壳换成 Tauri，安装更轻、后台更省，关窗后托盘里也能随时唤回来。","2026-08-08","DevPeek 开发实录：桌面壳从 Electron 迁到 Tauri，业务留在 Core，壳只负责窗口与系统能力，体积与资源占用更友好。",2,{"slug":56,"title":57,"summary":58,"date":59,"featured":8,"seoDescription":60,"series":10,"seriesOrder":61},"dev-build-log-sqljs-to-better-sqlite3","抓包历史为什么从 sql.js 迁到原生 SQLite","早期 DevPeek 用纯 JavaScript 版 SQLite 存抓包记录，联调一忙就占内存、列表发紧。我们换成系统级原生 SQLite 后，代理可以连续挂一整天，内存也稳。","2026-08-05","DevPeek 开发实录：抓包历史存储从 sql.js 换成原生 SQLite，解决长时间联调内存上涨、列表卡顿，让代理可以挂一整天。",1,{"slug":63,"title":64,"summary":65,"date":66,"featured":8,"seoDescription":67,"series":27,"seriesOrder":54},"mock-map-route-split-api","API 已迁新服务，前端还没发版？用「转发规则」联调","服务拆分后，前端仍请求旧 API，订单接口却已经迁到新服务。使用 DevPeek「转发规则」无需修改前端代码，即可把指定接口转发到新域名；若新服务未完成，还可以用 Mock 先跑通页面。","2026-07-26","微服务联调与 API 域名迁移：用 DevPeek「转发规则」做 API 转发，不改前端即可把旧域名请求指到新服务；服务迁移未完成时可用 Mock 先验页面。附接口联调 Demo。",{"slug":69,"title":70,"summary":71,"date":72,"featured":8,"seoDescription":73,"series":27,"seriesOrder":61},"api-param-encryption-debug","H5 接口参数加密？DevPeek 一键解密调试","联调时接口参数 AES 加密看不到明文？用 DevPeek 参数转换功能，填好密钥和 IV 就能自动解密，还能改参重放。","2026-07-18","用 DevPeek 参数转换功能解密 AES-GCM 加密的 H5 API 请求，支持双向转换、明文编辑、调试重放。",{"slug":75,"title":76,"summary":77,"date":78,"featured":79,"seoDescription":80,"series":81,"seriesOrder":54},"h5-debug-console-mock","H5 调试实战（二）：Android WebView 白屏排查，从 Console 远程定位到 Mock 验证","活动 H5 在部分 Android 设备中点击按钮后白屏，请求全部 200 却没有页面反馈。通过 DevPeek Console 获取线上 WebView 运行日志，远程 eval 确认 polyfill 覆盖问题，再使用 Mock 验证异常状态下的降级页面。","2026-07-13",true,"H5 调试实战第二篇：排查 Android WebView 白屏问题。通过 Console 远程调试定位 JavaScript polyfill 兼容问题，并结合 Mock 验证接口异常场景下的页面降级逻辑。","h5-debug",{"slug":83,"title":84,"summary":85,"date":86,"featured":79,"seoDescription":87,"series":81,"seriesOrder":61},"wechat-h5-storage-debug","H5 调试实战（一）：微信 H5 localStorage 调试，在电脑上修改真机缓存","微信 H5 换测试号后头像仍显示旧用户，抓包确认接口正常，却定位到 localStorage 遗留旧数据。本文记录一次真实联调过程，以及如何在电脑上查看、修改微信 WebView 的 localStorage、sessionStorage 和 IndexedDB。","2026-07-11","微信 H5 调试实战：通过真实案例定位 localStorage、sessionStorage、IndexedDB 导致的数据缓存问题，对比 vConsole、Remote Debug 等方案，介绍真机 WebView 调试思路。",{"slug":89,"title":90,"summary":91,"date":92,"featured":79,"seoDescription":93,"series":94,"seriesOrder":54},"why-we-built-devpeek-h5-debug","我们为什么做 DevPeek（二）：App 里那页 H5，在电脑上也能对着查","登录接口用参数转换啃下来了，活动页 H5 却在 App WebView 里才崩。真机 remote debug 折腾一圈，DOM 和抓包还是两拨窗口——于是把镜像和自研调试面板收进 DevPeek。","2026-07-10","DevPeek 起源系列第二篇：App 内嵌 H5 只在真机出问题、remote debug 与抓包割裂的联调痛点，以及调试 Tab 如何把页面镜像到电脑并用自研面板查 DOM、Console 与 Network。","origin",{"slug":96,"title":97,"summary":98,"date":99,"featured":79,"seoDescription":100,"series":94,"seriesOrder":61},"why-we-built-devpeek","我们为什么做 DevPeek（一）：HTTPS 解密了，Body 还是天书","版本日前那晚，TLS 早就解开了，改请求里一个字段却还要翻脚本加解密。于是萌生做一个把业务加解密收进代理的工具——DevPeek 从这儿开始。","2026-07-09","DevPeek 起源系列第一篇：联调时请求体加解密的手工折腾，以及为什么先做一款把业务加解密收进代理的工具。",1789537095380]