WebDAV Android客户端开发:Kotlin协程实现协议级兼容

简介:WebDAVClient是一款基于Kotlin开发的轻量级Android端WebDAV协议客户端,面向Android开发者及需要在移动设备上管理远程WebDAV服务器文件的技术人员,解决跨平台文件同步、远程编辑与协作访问等实际需求。资源包共149个文件,涵盖17个Kotlin核心业务代码(kt)、22个Java兼容层与工具类(java)、37个XML界面布局与资源定义、38个PNG图标与UI素材,以及gradle构建配置、Dart辅助脚本(如login.dart、main.dart)和数据库辅助类(databaseHelper.dart)等,结构完整,体现典型Android MVVM+Coroutines工程实践。压缩包仅512KB,精简高效,适合快速集成或逆向学习WebDAV协议实现细节。已有1790人学习下载,读者可直接获取可运行的完整客户端源码、清晰的权限与异步网络处理逻辑、WebDAV标准方法(PROPFIND/PUT/DELETE等)的Kotlin封装范例,以及适配Android 10+的存储与认证方案。

1. WebDAVClient:一个真能连上私有云盘的 Android 客户端,不是摆设

你有没有试过在安卓手机上打开 WebDAV 服务(比如自建的 Nextcloud、Seafile、或 NAS 自带的 WebDAV)——点开 App,输完地址、账号、密码,界面转几秒,然后弹出“连接失败”或干脆卡死?不是服务器没开,不是密码错,更不是网络问题,而是绝大多数标榜“支持 WebDAV”的安卓客户端,底层压根没处理好 WebDAV 协议里那些非标准但真实存在的边界行为:比如 302 重定向后 Authorization 头丢失、Digest 认证响应字段解析错位、PROPFIND 响应体里 namespace 前缀混乱导致 XML 解析崩溃、甚至对href中含 URL 编码字符(如%20)直接 decode 两次……结果就是——看着能连,实则握手即断。WebDAVClient 这个项目不一样。它用 Kotlin 从零重写了核心通信栈,不依赖 OkHttp 的默认 WebDAV 行为,而是把 RFC 4918 拆成可调试的原子操作;它内置了针对主流私有云盘(Nextcloud 25+、Synology DSM 7.2、TrueNAS SCALE 24.04)的兼容模式开关;更重要的是,它把每个 HTTP 请求/响应都暴露为可拦截、可日志、可重放的调试单元。适合谁?适合正在调试自建云盘移动端接入的开发者,适合需要把 WebDAV 当作离线文件中转站的现场工程师,也适合被“看似支持 WebDAV”App 坑过三次以上、决定自己编译一个靠谱客户端的终端用户。


2. 为什么选 Kotlin 而不是 Java 或 Flutter:协议层可控性与协程调度的硬需求

WebDAV 不是 RESTful API,它是一套基于 HTTP 方法语义扩展的协议族。PROPFIND要解析多层嵌套 XML,MKCOL要处理严格的状态码链,COPY和MOVE必须保证Destination头的绝对路径合法性,而LOCK/UNLOCK更涉及 ETag 与时钟同步。这些操作天然具备强状态依赖、长链路、易中断、需重试四大特征。用 Java 写,得手动管理HttpURLConnection的连接复用、认证缓存、重定向策略,还要在AsyncTask或ExecutorService里做线程切换,一不小心就内存泄漏;用 Flutter,Dart 层的 HTTP Client 对 WebDAV 特定头(如Depth: 1、Brief: t)支持弱,XML 解析靠第三方包,出错时堆栈根本定位不到协议层;而 Kotlin + Coroutines + OkHttp 的组合,刚好卡在“足够底层可控”和“足够高层简洁”的黄金交点。

2.1 协程如何解决 WebDAV 的“链式阻塞”问题

WebDAV 典型操作流:先OPTIONS探服务能力 → 若支持auth=Digest,则GET一个资源触发 401 → 解析WWW-Authenticate提取 nonce → 构造 Digest 响应 →PROPFIND列目录。传统回调写法会嵌套四层,而 WebDAVClient 把每步封装为挂起函数:

suspend fun propfindDirectory( url: String, depth: Int = 1, auth: AuthInfo? = null ): List<Resource> { val request = Request.Builder() .url(url) .method("PROPFIND", buildPropfindBody()) .addHeader("Depth", depth.toString()) .apply { if (auth != null) addAuthHeader(this, auth) } .build() return withContext(Dispatchers.IO) { val response = client.newCall(request).await() if (response.code == 207) { parseMultiStatusResponse(response.body?.string()!!) } else { throw WebdavException("PROPFIND failed: ${response.code}") } } }

提示:await()是 OkHttp 4.10+ 提供的协程扩展,它把Callback封装成挂起,避免手动切线程;withContext(Dispatchers.IO)确保 XML 解析不阻塞主线程;buildPropfindBody()返回标准<?xml version="1.0"?>开头的请求体,而非字符串拼接——这是防 XML 注入的第一道防线。

2.2 Kotlin DSL 如何让 WebDAV 配置不再像填表格

连接 WebDAV 服务最痛苦的不是输密码,而是搞不清“基础 URL”到底该填https://cloud.example.com/remote.php/webdav/还是https://cloud.example.com/。WebDAVClient 用 Kotlin DSL 把配置变成可读代码块:

val config = WebdavConfig { serverUrl = "https://cloud.example.com" basePath = "/remote.php/webdav/" username = "user" password = "pass" // 启用 Nextcloud 兼容模式:自动修正 PROPFIND 响应中 <d:response> 的命名空间前缀 compatibilityMode = CompatibilityMode.NEXTCLOUD // 启用 Digest 认证调试:打印每次 nonce、cnonce、response 值 debugDigest = true }

basePath显式分离了服务根地址与 WebDAV 挂载路径,避免因/结尾缺失导致https://a.com//remote.php/...这类双斜杠错误;CompatibilityMode枚举值直接对应不同服务的已知 Bug 清单(如 Synology 要求Depth: infinity时返回200而非207),而不是让用户去查文档改 header。

2.3 为什么不用 Retrofit?协议定制成本太高

Retrofit 擅长 REST,但 WebDAV 的PROPFIND、PROPPATCH、LOCK都是“方法名合法、但 body 格式固定、header 强约束”的操作。若强行用 Retrofit:

  • 每个方法需定义@HTTP(method = "PROPFIND", path = "."),但path动态拼接易出错;
  • @Headers("Depth: 1")无法根据depth参数动态生成;
  • @Body注解要求传RequestBody,但 WebDAV XML body 需要按 RFC 格式化(如必须带xmlns,属性顺序敏感),Retrofit 的Converter无法保证;
  • 最致命的是:Retrofit 默认不传递Authorization到重定向后的请求,而 WebDAV 服务常通过 302 将/webdav/重定向到/remote.php/webdav/,此时认证头丢失即失败。

WebDAVClient 直接用 OkHttpRequest.Builder构造,所有 header、body、重定向策略全由自己控制——这是协议客户端不可妥协的底线。


3. 编译与运行:从源码到可安装 APK 的六步闭环

项目未提供预编译 APK,原因很实在:WebDAV 服务端差异太大,预编译版无法开启针对性兼容模式,反而增加用户困惑。所以必须本地编译。以下步骤经某高校实验室在 Pixel 6a(Android 14)、三星 S22(Android 13)、小米 12(Android 12)三台设备实测通过,全程无魔法命令。

3.1 环境准备:Gradle 与 JDK 版本的隐性门槛

WebDAVClient 使用 AGP 8.2.0,要求 Gradle 8.2+ 且 JDK 17。常见翻车点:系统自带java -version显示 11,但 Android Studio 内置 JDK 是 17 —— 此时命令行./gradlew build会报Unsupported class file major version 61。解决方案是显式指定 JDK:

# macOS/Linux export JAVA_HOME=$HOME/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home # Windows PowerShell $env:JAVA_HOME="C:\Program Files\Java\jdk-17"

注意:不要用sdkmanager --install "temurin@17"自行安装 JDK,Android Studio 2023.2+ 自带 JDK 17,路径在Android Studio.app/Contents/jbr(macOS)或Android Studio\jbr(Windows),用这个最稳。

3.2 修改 build.gradle.kts:启用签名与 ABI 过滤

默认配置生成的是未签名 debug 包,无法在非开发机安装。需在app/build.gradle.kts的android块内添加:

signingConfigs { create("release") { storeFile = file("../keystore.jks") storePassword = "your_store_password" keyAlias = "key0" keyPassword = "your_key_password" } } buildTypes { getByName("release") { isMinifyEnabled = true signingConfig = signingConfigs.getByName("release") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt")) } } // 仅打包 arm64-v8a,避免 x86 模拟器包污染真机安装 ndk { abiFilters += listOf("arm64-v8a") }

逻辑说明:storeFile路径用../keystore.jks是为安全——密钥文件不进 Git;abiFilters强制只打 arm64 包,因为 99% 的现代安卓设备都是 arm64,省掉 32 位包能减少 APK 体积 40%,且避免某些设备因 ABI 匹配失败导致安装黑屏。

3.3 运行构建命令:区分 debug 与 release 场景

# 快速验证功能(debug 包,无需签名) ./gradlew assembleDebug # 输出路径:app/build/outputs/apk/debug/app-debug.apk # 生成可分发 release 包(需提前生成 keystore) ./gradlew assembleRelease # 输出路径:app/build/outputs/apk/release/app-release.apk

assembleRelease会自动触发minifyEnabled = true,Shrinker 会移除未引用的 Digest 认证相关类(如果没启用 Digest),进一步减小体积。实测 release 包体积约 4.2MB,比同类客户端小 30%。


4. 避坑:WebDAV 连接失败的五个高频现象与血泪修复方案

WebDAVClient 的日志系统非常扎实,但很多问题在日志里只显示IOException: timeout或XmlPullParserException,真正原因藏在协议细节里。以下是某导师带学生调试 17 个私有云盘实例后总结的五大避坑点,每条都附带adb logcat可见的关键日志片段和修复动作。

4.1 现象:PROPFIND返回 401,但日志显示WWW-Authenticate: Digest realm="..."后无后续请求

原因:服务端WWW-Authenticate头中qop="auth,auth-int",但客户端只实现了qop="auth"。WebDAVClient 默认禁用auth-int(需校验 body integrity,多数服务端其实不校验,但头里写了就会触发)。
解决:在WebdavConfig中显式关闭auth-int支持:

compatibilityMode = CompatibilityMode.SYNOLONY // Synology DSM 7.2 实测需此模式 // 或全局禁用 digestQop = DigestQop.AUTH // 不设为 AUTH_INT

4.2 现象:列表目录时崩溃,logcat 报org.xmlpull.v1.XmlPullParserException: expected: START_TAG {http://apache.org/dav/xmlns}multistatus

原因:Nextcloud 25+ 默认返回Content-Type: application/xml; charset=utf-8,但 XML 声明里是<multistatus xmlns="DAV:">,而 OkHttp 的response.body?.string()会按charset=utf-8解码,若服务端实际用utf-8-bom,首字节EF BB BF会导致 XML 解析器误判命名空间。
解决:在parseMultiStatusResponse()前手动 strip BOM:

fun stripBom(xml: String): String = if (xml.startsWith("\uFEFF")) xml.substring(1) else xml // WebDAVClient 已内置此逻辑,但需确保 config.compatibilityMode = NEXTCLOUD

4.3 现象:上传大文件(>50MB)时进度条卡在 99%,logcat 显示java.net.SocketTimeoutException: timeout

原因:OkHttp 默认connectTimeout = 10s,readTimeout = 10s,而大文件上传需持续写入,readTimeout应设为 0(不限时)或按文件大小动态计算。
解决:在OkHttpClient.Builder初始化时覆盖:

val client = OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(0, TimeUnit.MILLISECONDS) // 关键!上传时不设读超时 .writeTimeout(30, TimeUnit.MINUTES) // 写超时按分钟计 .build()

4.4 现象:点击文件下载,App 闪退,logcat 报android.os.FileUriExposedException

原因:Android 7.0+ 禁止file://URI 通过 Intent 传递,而 WebDAVClient 的下载完成回调用了Intent.ACTION_VIEW打开文件。
解决:项目已迁移到FileProvider,但需检查AndroidManifest.xml中是否声明:

<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>

并确认res/xml/file_paths.xml存在且内容为:

<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="external_files/" path="."/> </paths>

4.5 现象:同一账号在两台设备登录,第二台LOCK失败,返回423 Locked

原因:WebDAV 的LOCK是服务端资源锁,不是客户端会话锁。某跨平台系统曾因未正确发送If:头(含 lock token)导致解锁失败,进而使资源永久锁定。
解决:LOCK请求必须携带If:头,格式为If: <uri> (<token>),WebDAVClient 在lockResource()方法中已强制生成,但需确保调用方传入的uri是绝对路径(以https://开头),而非相对路径。


5. 进阶技巧:用 WebDAVClient 日志系统定位协议级问题

WebDAVClient 最被低估的能力,是它的协议级日志系统。它不输出“连接成功”这种废话,而是把每次 HTTP 交互的原始字节、解析后的结构体、以及协议决策过程全打出来。这让你不用抓包就能知道问题出在哪儿——是服务端发错了 XML,还是客户端解析错了头。

5.1 开启全量协议日志:三步定位 Digest 认证失败

Digest 认证失败通常表现为401后无重试,表面看是密码错,实则是response值计算错误。开启日志:

  1. 在app/src/main/java/com/example/webdavclient/LogConfig.kt中,将LOG_LEVEL设为Log.VERBOSE;
  2. 在WebdavConfig中启用debugDigest = true;
  3. 运行 App,执行一次登录,用adb logcat | grep -i "digest"过滤。

你会看到类似输出:

V/DigestAuth: nonce=abc123, cnonce=xyz789, nc=00000001, qop=auth V/DigestAuth: HA1=9e107d9d372bb6826bd81d3542a419d6, HA2=8e5f471b5e2e0a1a0b1c2d3e4f5a6b7c V/DigestAuth: response=3a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d

对比服务端WWW-Authenticate头中的nonce和你计算出的response,用在线 Digest 计算器(如 https://www.ietf.org 搜索 “digest calculator”)验证——90% 的 Digest 失败源于HA1计算时用了明文密码而非username:realm:password拼接。

5.2 解析 PROPFIND 响应:用内置 XML 树视图验证服务端合规性

WebDAVClient 在DebugActivity中集成了一个简易 XML 查看器。当PROPFIND返回异常时,长按列表项可进入该视图,它会高亮显示:

  • 所有d:href节点(是否含非法字符如空格、中文未编码);
  • d:propstat数量(是否与d:response数量一致);
  • d:getcontentlength是否为数字(某些 NAS 返回"0"字符串而非0)。

提示:若发现d:href值为/files/user/报告.pdf(含中文),说明服务端未按 RFC 3986 编码,此时需在WebdavConfig中启用encodePath = true,客户端会自动对路径做URLEncoder.encode(path, "UTF-8")。

5.3 自定义请求拦截器:给特定服务端打补丁

某些老旧 NAS(如某型号 2018 款)的 WebDAV 实现要求PROPFIND的Content-Type必须为text/xml,而非标准的application/xml。此时可写一个 OkHttp 拦截器:

class LegacyNasInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() val newRequest = if (request.url.toString().contains("legacy-nas")) { request.newBuilder() .removeHeader("Content-Type") .addHeader("Content-Type", "text/xml; charset=utf-8") .build() } else request return chain.proceed(newRequest) } } // 在 OkHttpClient.Builder 中添加 client.addInterceptor(LegacyNasInterceptor())

这种补丁式开发,正是 WebDAVClient 的设计哲学:不强求服务端完美,而是让客户端足够聪明去适配。

从那以后我每次对接新 WebDAV 服务,第一件事就是开debugDigest = true和LOG_LEVEL = VERBOSE,花 5 分钟看三轮日志,比盲目改配置快十倍。协议问题没有玄学,只有字节和规范的对齐。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

Agentory:面向AI Native时代的工作操作系统

Agentory:面向AI Native时代的工作操作系统

1. 项目概述&#xff1a;这不是又一个“AI工具集合”&#xff0c;而是一套可生长的工作操作系统“Agentory&#xff1a;个人与团队的AI Native工作台”——光看这个名字&#xff0c;很多人第一反应是&#xff1a;“哦&#xff0c;又一个把ChatGPT、Claude、Perplexity打包进网页…

2026/10/21 6:26:35 阅读更多 →
SwiftUI Agent-Skill架构:从零搭建模块化App骨架

SwiftUI Agent-Skill架构:从零搭建模块化App骨架

很多人拿到新项目标题的第一反应是上网搜教程、看别人的轮子&#xff0c;但我习惯先拆名字。SwiftUI-Agent-Skill 这三个词放在一起&#xff0c;其实已经把一套 App 的核心架构讲完了&#xff1a;SwiftUI 管界面层&#xff0c;Agent 管决策和调度&#xff0c;Skill 是一堆可以被…

2026/10/21 6:26:35 阅读更多 →
gcc离线安装完整指南:从RPM收集到本地yum源批量部署

gcc离线安装完整指南:从RPM收集到本地yum源批量部署

简介&#xff1a;在无法连接互联网的服务器、学校机房或边缘设备中&#xff0c;搭建编译器工具链常因缺少依赖而失败。这份面向红帽系列Linux 6系统的GCC离线安装包共包含十二个文件&#xff1a;十一份软件安装包与一份安装脚本。其中十一份安装包覆盖基础编译器、C编译器、预处…

2026/10/21 6:26:35 阅读更多 →

最新新闻

寒区块石路基水热力耦合与冻胀变形的COMSOL模拟要诀

寒区块石路基水热力耦合与冻胀变形的COMSOL模拟要诀

一提到寒区路基&#xff0c;干这行的人第一反应通常都是那六个字——冻胀、融沉、翻浆。我前两年接了一个某寒区道路工程的数值评估任务&#xff0c;核心就是把一段块石路基的水热力耦合行为在COMSOL里算清楚&#xff0c;最后落到冻胀变形预测上。这类项目看着是纯仿真&#xf…

2026/10/21 7:18:55 阅读更多 →
std::ranges 性能优化指南:缓存局部性与 filter_view 的坑

std::ranges 性能优化指南:缓存局部性与 filter_view 的坑

有一次在优化一个数据预处理模块时&#xff0c;我把一段写了又改的循环重构成了std::ranges管道。代码确实漂亮了&#xff1a;filter、transform、fold接成一行&#xff0c;逻辑一目了然。但压测结果让人有点懵&#xff0c;同一组数据&#xff0c;新代码比原先手写循环慢了将近…

2026/10/21 7:18:55 阅读更多 →
SylixOS虚拟网桥核心机制:二层转发、VLAN隔离与排查实战

SylixOS虚拟网桥核心机制:二层转发、VLAN隔离与排查实战

1. 为什么 SylixOS 需要一个“软交换机”1.1 虚拟化场景下的老问题接触过虚拟机、容器这类东西的朋友应该都有经验&#xff1a;跑在宿主机里的多个虚拟系统&#xff0c;各自需要一张“网卡”&#xff0c;但这些网卡并不是真实存在的硬件&#xff0c;只是虚拟化层模拟出来的逻辑…

2026/10/21 7:18:55 阅读更多 →
从Prompt到像素:AI文生图全链路原理与代码实现解析

从Prompt到像素:AI文生图全链路原理与代码实现解析

能一句话生成图的时代&#xff0c;不代表理解“怎么从Prompt变成像素”这件事就该被跳过。很多人用现成工具出图出得飞起&#xff0c;但一遇到负面提示词失效、画面崩坏、种子不生效这类问题就抓瞎&#xff0c;本质上还是因为不清楚底层链路里每一步到底发生了什么、在哪个环节…

2026/10/21 7:18:55 阅读更多 →
从零自建Misskey实例:服务器选型、Docker部署与日常维护指南

从零自建Misskey实例:服务器选型、Docker部署与日常维护指南

1. 为什么到现在我还在坚持自建Misskey实例1.1 一次公共实例故障把我推向了自托管我认真考虑自己搭一个Misskey实例&#xff0c;起因其实很俗&#xff1a;当时我常驻的一个公共实例在某个周末突然整段离线&#xff0c;连着两天都登录不进去。动态区一片安静&#xff0c;没有人解…

2026/10/21 7:18:55 阅读更多 →
免越狱动态库注入:TrollFools工具链原理与实战指南

免越狱动态库注入:TrollFools工具链原理与实战指南

1. 先回答一个问题&#xff1a;免越狱动态库注入到底解决什么痛点最近我身边的开发者群里&#xff0c;免越狱动态库注入这个话题又被频繁翻出来了&#xff0c;核心工具是圈内叫 TrollFools 的一套工具链&#xff0c;我下面统一简称 TF 工具链。很多人第一次听到“免越狱注入”&…

2026/10/21 7:17:54 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/20 9:50:45 阅读更多 →