纯Kotlin实现WebDAV协议Android客户端:从XML解析到断点续传
简介WebDAVClient 是一份采用 Kotlin 语言编写的 Android 端 WebDAV 客户端源码面向需要远程管理服务器文件的开发者和网络存储用户。客户端借助 WebDAV 扩展出的各类请求方法实现远程目录浏览、文件上传下载、重命名、移动以及删除等常用功能并覆盖身份认证、权限申请与异步任务处理等关键环节。资源压缩包共含一百四十九个文件整体大小约五百一十二千字节主体由 Kotlin 与 Java 源码、XML 布局与配置、PNG 图标以及 Gradle 构建脚本组成另有一些 Dart 文件便于观察工程与跨端模块的组合方式。目前已有 1788 人学习下载读者能够从中获得网络请求封装、界面资源组织、构建脚本配置和错误处理思路也能体会 Kotlin 空安全特性在真实客户端中的实际运用。整体体量紧凑、目录清晰工程内部包结构划分明确有利于按模块阅读与二次开发配合 WebDAV 服务端调试时可以较快梳理请求流程与界面刷新的联动关系适合作为 Android 开发者接入 WebDAV 服务或进一步扩展个人文件管理工具的参考样本。1. 不靠厂商 SDK一个纯 Kotlin 的 WebDAV 协议 Android 客户端WebDAVClient 是一个用 Kotlin 从零实现的 WebDAV 协议 Android 客户端。如果你手里有一台支持 WebDAV 协议的 NAS 或网盘又不想被厂商 App 的功能和广告绑住这个工程能给你一套完全可控的替代方案——从 PROPFIND 列目录、XML 响应解析到文件上传下载和 ViewModel 状态管理全部源码可见、可改。它适合两类人一类是想在手机上获得轻量文件管理体验的用户另一类是正在学 Kotlin 网络编程的开发者——这个工程能把「协议请求构造」「XML 解析」「协程状态管理」三件事串在同一条链路上边读边跑比看零散教程有效得多。2. 工程结构拆解Kotlin OkHttp 的分层设计2.1 为什么是这个组合OkHttp XmlPullParser ViewModelWebDAV 本质上是 HTTP 协议的方法扩展标准化的请求方法有 PROPFIND、PROPPATCH、MKCOL、COPY、MOVE、LOCK、UNLOCK 这些。网络层用 OkHttp 是成本最低的选择——通过Request.Builder().method(PROPFIND, body)就能构造任意 WebDAV 方法不需要引入 Retrofit 这种面向 RESTful JSON 的框架因为 WebDAV 的响应体是multistatus类型的 XMLRetrofit 的注解式接口在这里反而碍手碍脚。XML 解析用 Android 自带的 XmlPullParser而不是 DOM 解析器。原因很实际如果某个目录下有上万个文件PROPFIND 对应的 XML 响应可能有几十 MBDOM 会把整棵 XML 树加载进内存移动端直接 OOMXmlPullParser 是流式的边读边丢内存占用恒定。状态管理上工程用的是 ViewModel StateFlow配合 RecyclerView 渲染文件列表。整个工程没有上 Hilt 这类重量级依赖注入框架构造函数手动传依赖——核心对象WebDavClient本来就持有一个OkHttpClient实例手动构造反而让依赖链更直观。2.2 目录结构和分层职责app/src/main/java/com/example/webdavclient/ ├── MainActivity.kt ├── data │ ├── model/RemoteFile.kt │ ├── remote/WebDavClient.kt │ ├── remote/XmlParser.kt │ ├── remote/AuthInterceptor.kt │ └── repository/FileRepository.kt ├── domain/FileItem.kt ├── ui │ ├── login/LoginViewModel.kt │ ├── filelist/FileListAdapter.kt │ ├── filelist/FileListViewModel.kt │ └── transfer/TransferViewModel.kt └── util/UriUtils.ktdata/model/RemoteFile.kt是协议层的 DTO字段和 WebDAV 响应里的属性一一对应data/remote/是协议封装层WebDavClient负责发送各种方法请求XmlParser负责把 multistatus 响应转成对象列表AuthInterceptor拦截所有请求并附加 Authorization 头data/repository/FileRepository.kt是仓库层给 UI 提供干净的挂载、列表、下载接口UI 层不需要知道 PROPFIND 是什么。ui/下按页面分包LoginViewModel 负责登录态FileListViewModel 持有当前目录的文件列表状态TransferViewModel 管理下载队列的进度。util/UriUtils.kt专门处理 URL 拼接和路径编码——这个工具类在 WebDAV 客户端里比想象中更重要后面避坑章节会展开。RemoteFile和FileItem是两个不同的类前者是协议层原始数据后者是加入了isDirectory、childrenLoaded等 UI 状态的领域对象。仓库层负责两者转换这样协议层的变化不会波及 UI 层。分层清晰的好处是协议实现和 UI 渲染可以并行改某天想换一种 WebDAV 库只要data/remote/的接口签名不变上面的层完全不用动。2.3 关键配置文件build.gradle.kts 与 AndroidManifest.xmldependencies { implementation(com.squareup.okhttp3:okhttp:4.12.0) implementation(org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3) implementation(androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0) implementation(androidx.recyclerview:recyclerview:1.3.2) }依赖数量控制得很少这是刻意的。OkHttp 负责网络协程负责把 IO 调度到后台线程ViewModel 负责状态RecyclerView 负责列表。核心业务逻辑全部自己写不引入 WebDAV 封装库——自己实现协议层可以完全掌控错误处理和日志输出排查服务器兼容性问题时这个优势非常明显。AndroidManifest 里同样有值得注意的配置uses-permission android:nameandroid.permission.INTERNET / application android:usesCleartextTraffictrue ... /applicationusesCleartextTraffictrue是开发 WebDAV 客户端绕不开的设置。很多 NAS 和私有网盘的 WebDAV 服务只开了 HTTP 而不是 HTTPS而 Android 9 之后默认禁止明文流量不加这个开关连本机局域网地址都会直接抛 CLEARTEXT 异常。开发阶段开全局开关可以正式发布建议用networkSecurityConfig只放行内网 IP 段而不是全部放开。3. 核心链路PROPFIND 列目录与 XML 解析3.1 PROPFIND 请求列目录的协议基础WebDAV 里列目录不是用 GET而是用 PROPFIND。这个方法的请求体会声明「我想要哪些属性」响应体会返回当前资源以及根据 Depth 头子资源的信息。Depth 是最容易用错的参数Depth: 0只看当前 URL 对应资源本身Depth: 1列出直接子项infinity是递归整棵子树。实际开发里几乎不用 infinity一个稍微大点的目录树会把服务器打挂客户端也会等超时。private val client: OkHttpClient OkHttpClient.Builder() .addInterceptor(AuthInterceptor(user, password)) .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build() fun propfind(url: String, depth: Int 1): ListRemoteFile { val body ?xml version1.0 encodingutf-8 ? d:propfind xmlns:dDAV: d:prop d:displayname/ d:resourcetype/ d:getcontentlength/ d:getlastmodified/ /d:prop /d:propfind .trimIndent() val request Request.Builder() .url(url) .method(PROPFIND, body.toRequestBody(application/xml.toMediaType())) .header(Depth, depth.toString()) .build() client.newCall(request).execute().use { response - if (!response.isSuccessful) throw IOException(HTTP ${response.code}) return parser.parseList(response.body?.charStream()!!) } }请求体里显式声明只需要displayname、resourcetype、getcontentlength、getlastmodified四个属性这是很关键的优化点。不声明属性的话服务器会把创建时间、权限位、etag、所有者等等全量返回响应体积差好几倍。depth参数默认 1某些服务器对 depth2 会返回 403调用方要捕获这个异常并提示用户当前目录过大、不支持预览。AuthInterceptor的逻辑不复杂在拦截器里给每个请求加上Authorization: Basic base64(user:pass)头。这个细节放在拦截器而不是每个方法里写是因为 WebDAV 的每个操作列表、下载、上传、删除都要认证重复代码放到拦截器里一次搞定。3.2 Multistatus 响应解析逐标签状态机PROPFIND 的响应结构是固定的最外层multistatus里面包着若干个response每个response对应一个文件或目录。所有路径和属性都嵌在 XML 节点里所以解析器的核心任务是维护一个「当前正在解析哪个文件」的状态。fun parseList(input: Reader): ListRemoteFile { val parser XmlPullParserFactory.newInstance().newPullParser() parser.setInput(input) var current: RemoteFile? null var text val result mutableListOfRemoteFile() while (parser.eventType ! XmlPullParser.END_DOCUMENT) { when (parser.eventType) { XmlPullParser.START_TAG - when (parser.name) { response - current RemoteFile() resourcetype - current?.isDirectory true } XmlPullParser.TEXT - text parser.text.orEmpty() XmlPullParser.END_TAG - when (parser.name) { href - current?.path decodeHref(text) displayname - current?.name text getcontentlength - current?.size text.toLongOrNull() ?: 0L getlastmodified - current?.lastModified parseDavTime(text) response - { current?.let { result.add(it) } current null } } } parser.next() } return result }逻辑说明START_TAG 遇到response就新建一个RemoteFile对象遇到resourcetype就标记为目录——注意这里不用等文本节点因为目录的标识是d:collection/自闭合子标签START_TAG 时就能捕获。END_TAG 时把 TEXT 阶段累积的文本写进当前对象response结束后把对象加入结果列表并重置当前状态。这里有两个细节容易翻车。一是parser.name拿到的名字不带命名空间前缀解析时判断的是本地名所以不管服务器返回d:response还是D:responsename都是response不用额外处理大小写。二是href可能返回 URL 编码路径需要调用decodeHref先做一次 URLDecode否则进入下一级目录时拼接 URL 会双重编码。3.3 上传与下载的流式实现下载的常见错误是body.bytes()一把梭。一个 300MB 的视频文件bytes()会把它整个读进堆内存App 直接 OOM。正确做法是从响应体拿 source 流边读边写入目标输出流fun download(path: String, output: OutputStream) { val request Request.Builder() .url(buildUrl(path)) .get() .build() client.newCall(request).execute().use { response - if (!response.isSuccessful) throw IOException(HTTP ${response.code}) val source response.body?.source() ?: return val sink output.buffered() source.readAll(sink) sink.flush() } }output是调用方传入的流可以指向FileOutputStream也可以指向 ContentResolver 打开的 Uri 流这样下载到 App 私有目录还是公共下载目录由上层决定协议层不关心。流式处理的优点是内存占用恒定不管文件多大OkHttp 内部是按 segment 分批读取的。上传的逻辑对称把 InputStream 包成 OkHttp 的 RequestBodyfun upload(path: String, input: InputStream) { val body input.toRequestBody(application/octet-stream.toMediaType()) val request Request.Builder() .url(buildUrl(path)) .put(body) .build() client.newCall(request).execute().use { response - if (!response.isSuccessful) throw IOException(HTTP ${response.code}) } }这里用 PUT 而不是 POST是 WebDAV 的语义要求PUT 的语义是「把请求体写到 URL 指定位置」服务器会创建新文件或覆盖同名文件POST 没有固定语义很多 WebDAV 服务器根本不支持 POST 写文件。toRequestBody内部会流式读取 InputStream不会把整个文件加载进内存所以上传大文件时同样不会 OOM。4. 跑通与联调Android 构建配置与 WebDAV 服务器连接4.1 构建前准备Gradle 与 SDK 版本工程要求 JDK 17配合 Android Studio 打开后直接同步即可。compileSdk用 34 是因为最新稳定版 Android 14 的 API 级别minSdk设 24 是个性价比比较高的下限覆盖了绝大多数在用设备同时保证 OkHttp 4.x 的 Kotlin 标准库和现代存储 API 都能用。android { namespace com.example.webdavclient compileSdk 34 defaultConfig { applicationId com.example.webdavclient minSdk 24 targetSdk 34 versionCode 1 versionName 1.0 } buildFeatures { viewBinding true } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { jvmTarget 17 } }buildFeatures.viewBinding true是工程里大量使用的功能——所有页面都通过 ViewBinding 拿控件引用省掉了 findViewById 模板代码。compileOptions和kotlinOptions的 JVM 目标必须一致都设 17否则 Kotlin 编译出来的字节码版本和 Java 编译配置不匹配运行时大概率报 VerifyError。4.2 连接配置地址格式与 Basic 认证登录界面收集三样信息服务器地址、用户名、密码。地址格式常见的有两种一种是无子路径的http://192.168.1.10:5005/dav/另一种是逻辑路径http://example.com/remote.php/dav/files/username——后者的子路径决定了文件根目录的挂载点。连接前先确认协议头写全192.168.1.10:5005这种省略http://的写法在 OkHttp 里会直接抛 MalformedUrlException。Basic 认证的构造方式比较直接val credentials Base64.encodeToString( ${username}:${password}.toByteArray(Charsets.UTF_8), Base64.NO_WRAP ) val authHeader Basic $credentialsBase64.NO_WRAP参数很关键——默认的 Base64 编码会在超过 76 个字符时插入换行符Authorization 头里出现换行会导致服务器解析失败。密码不要明文存 SharedPreferences工程里用的是 EncryptedSharedPreferences 包装一层但上线前更稳妥的做法是接入 Android Keystore 生成不可导出的密钥来加密账号信息。4.3 联调验证先跑通列目录再扩展拿到源码第一步不是急着编译 APK而是先用电脑命令行验证服务器本身是否可用。这个习惯能帮你快速区分「服务器问题」和「客户端问题」。curl -X PROPFIND --user user:pass -H Depth: 1 http://192.168.1.10:5005/dav/这条命令会返回一串 XML根节点是 multistatus里面能看到根目录下的文件列表和各自的属性。如果 curl 能正常返回而 App 报错问题在客户端代码如果 curl 都超时先检查服务器端口是否放行、WebDAV 服务是否开启。curl 验证通过后再在 Android Studio 里跑 App打开 Logcat 过滤 OkHttp 的日志观察实际发出的 PROPFIND 请求和响应码。联调时建议按这个顺序验证先登录成功拿到 200再列根目录看到文件树再点进子目录确认路径拼接正确最后测下载上传。每一步都有明确的排查信号。比如登录成功了但列表空八成是 Depth 头的问题列表能出但点进子目录 404基本是 URL 编码或路径拼接的 bug直接看UriUtils里的拼接逻辑即可。5. 避坑指南WebDAV 客户端最常见的四个坑5.1 中文文件名 URL 编码翻车现象英文目录一切正常一旦文件名或目录名含中文服务器返回 404 或 400。原因URL 里直接拼了未编码的中文或者对整条 URL 做URLEncoder.encode时把/也编码成了%2F服务器无法区分路径分隔符和文件名字符。解决按路径段编码只编码文件名分段保留分隔符。URLEncoder.encode会把空格编码成而 WebDAV 服务器通常只认%20所以要手工替换fun buildUrl(base: String, path: String): String { val encoded path.split(/) .joinToString(/) { segment - URLEncoder.encode(segment, UTF-8) .replace(, %20) } return base.trimEnd(/) / encoded.trimStart(/) }5.2 Depth 设成 infinity 导致请求超时现象点击某个目录后一直转圈最终抛出SocketTimeoutException日志里能看到 read timeout。原因把 Depth 设成了 infinity服务器会递归遍历整个目录树文件多、目录深时数据量呈指数级增长客户端 30 秒读不完就超时了。解决默认只用Depth: 1拉当前层。需要递归时自己实现调度逐层调用 propfind用协程并发请求各子目录每层限制并发数并记录已加载的路径集合避免重复请求。5.3 大文件下载 OutOfMemoryError现象下载几百 MB 的视频文件进度走一半 App 崩溃崩溃日志指向OutOfMemoryError。原因下载代码用了response.body?.bytes()或readBytes()把整个文件一次性读进堆内存。移动端堆内存通常只有几百 MB视频文件轻松撑爆。解决始终用流式读取第 3.3 节的写法是唯一正确姿势。另外下载任务要放到后台线程协程里用Dispatchers.IO不能直接在主线程同步执行网络请求。5.4 明文流量被系统拦截现象连接局域网 NAS 地址时报CLEARTEXT communication not permitted但同一地址在电脑浏览器能正常打开。原因Android 9 起默认禁止应用发起明文 HTTP 请求如果 WebDAV 服务没有配置 HTTPS请求在系统层就被拦截了。解决开发阶段在 manifest 里打开android:usesCleartextTraffictrue正式发布换成 networkSecurityConfig只对局域网网段放行明文。要注意的一点是部分 NAS 支持 HTTPS 但证书是自签名的这时还要处理 SSL 证书校验不能全局信任否则有中间人风险。6. 二次开发实战给下载模块加上断点续传断点续传是移动网络场景下最值得做的改造。思路是用 Range 请求头告诉服务器「我从第 N 个字节开始传」服务器返回 206 Partial Content本地用 RandomAccessFile 从断点位置写入。fun resumeDownload(url: String, file: File, offset: Long) { val request Request.Builder() .url(url) .header(Range, bytes$offset-) .get() .build() client.newCall(request).execute().use { response - if (response.code ! 206 response.code ! 200) { throw IOException(HTTP ${response.code}) } val source response.body!!.source() val raf RandomAccessFile(file, rw) raf.seek(offset) val buffer Buffer() while (true) { val read source.read(buffer, 8192L) if (read -1L) break raf.write(buffer.readByteArray(read)) } raf.fd.sync() raf.close() } }关键在一头一尾请求头里的bytes$offset-决定了服务器从哪里开始返回数据文件写入前的raf.seek(offset)决定了本地从哪个位置落盘两者必须一致。source.read(buffer, 8192L)每轮最多读 8KB返回 -1 表示流结束循环跳出。写入完成后调用raf.fd.sync()确保数据真正落盘避免进程被系统回收时缓存丢失。验证方法很直接下载一个大文件到一半杀掉进程记录已下载的大小重新启动同一个任务把记录的 size 作为 offset 传入观察服务端收到的请求头里 Range 值是否正确以及生成的文件能否通过完整性校验。可以在服务器访问日志里确认——如果 Range 头的起始值和你记录的断点一致说明整条链路是通的。我第一次做断点续传时图省事用FileOutputStream(append true)直接接流结果 offset 记录的是「期望的字节数」文件末尾实际却多写了一段两个值对不上文件校验永远失败。后来换成 RandomAccessFile.seek 才意识到断点续传本质是「协议状态同步」问题不是 IO 写法问题——头尾两处状态必须对齐资源才能无缝拼接。从那以后我每次写下载模块都强制把断点状态持久化到数据库再动手写 IO。这套工程源码拿到手之后建议先按第 4 章的 curl 验证顺序把列表跑通再逐步改造自己的传输逻辑希望帮到你。本文还有配套的精品资源点击获取

相关新闻

用KS检验自动选择最优核密度估计带宽:Python实现与原理详解

用KS检验自动选择最优核密度估计带宽:Python实现与原理详解

1. 为什么需要“最优”的核密度估计先说个场景。你在做数据分析时,拿到一堆样本数据——比如某城市一小时内所有网约车订单的等待时长,或者某工厂生产线上某批次零件的直径偏差。你想知道这些数据整体上服从什么分布,还想画出它的概率密度曲线…

2026/10/17 5:29:25 阅读更多 →
核密度估计与KS检验:用统计指标自动确定最优带宽的实践

核密度估计与KS检验:用统计指标自动确定最优带宽的实践

1. 核密度估计的核心逻辑与为什么非要追求“最优”很多人第一次接触核密度估计(Kernel Density Estimation,KDE)时,第一反应是:这不就是一个平滑版的直方图吗?把小区间里的频数数一数,再除以区间…

2026/10/17 5:29:25 阅读更多 →
在Egg中使用MongoDB及Mongoose:从连接配置到数据模型落地

在Egg中使用MongoDB及Mongoose:从连接配置到数据模型落地

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

2026/10/17 5:29:25 阅读更多 →

最新新闻

深入浅出Linux网卡驱动:DMA、NAPI与多队列调优

深入浅出Linux网卡驱动:DMA、NAPI与多队列调优

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

2026/10/17 6:04:33 阅读更多 →
glibc2.27堆题沙箱绕过:tcache、ORW和setcontext实战解析

glibc2.27堆题沙箱绕过:tcache、ORW和setcontext实战解析

从glibc 2.27开始,堆题的常规打法变得很机械:tcache几乎没有限制,double free和poisoning基本是“一键操作”。可一旦题目在堆漏洞外面又套了一层seccomp沙箱,整条利用链就得重新想。你辛辛苦苦把__free_hook改成了system&#xf…

2026/10/17 6:04:33 阅读更多 →
基于MP2642GR-Z与TM4C129EKCPDT的电池主动均衡方案设计与实现

基于MP2642GR-Z与TM4C129EKCPDT的电池主动均衡方案设计与实现

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

2026/10/17 6:04:33 阅读更多 →
储能电池均衡方案选型与MP2642GR-Z+STM32实战

储能电池均衡方案选型与MP2642GR-Z+STM32实战

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

2026/10/17 6:04:33 阅读更多 →
Matlab插值算法手写实验全解析:Lagrange、Newton与三次样条

Matlab插值算法手写实验全解析:Lagrange、Newton与三次样条

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

2026/10/17 6:04:33 阅读更多 →
三角分解法与高斯迭代法:线性方程组求解的工程实现与排错指南

三角分解法与高斯迭代法:线性方程组求解的工程实现与排错指南

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

2026/10/17 6:03:33 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →