FileBrowser: 一款文件服务器

FileBrowser: 一款文件服务器#

我曾经想过组一台 NAS, 但仔细想想, 这简直是在创造需求而非解决问题, 事实上我可能根本用不上 NAS, 大部分时间只需要一个运行在树莓派上简单的文件服务器就够了.

对于这种运行在 linux 上的服务程序, 我有自己的一套挑选标准:

  • 应当是使用原生语言编写、无运行时的, 例如使用 Rust, Go, C/C++ 等, 最好是单一可执行文件;
  • KISS, 即 Keep It Simple & Stupid, 功能克制, 页面简单, 只做好自己该做的事, 不要加一堆用不到的功能, 让软件变得臃肿不堪.

综合来说, 我最后选了 File Browser, 因为它是用 Go 编写的, 只有最简单的文件上传、下载、(图片) 预览等功能, 不像其他文件服务器大包大揽, 还非得加入在线编辑之类用不上的功能; 另外, 它的网页也相当耐看.

配置服务#

下载它的可执行文件后, 在一个无关紧要的空目录运行 ./filebrowser, 程序将在此处生成一个 filebrowser.db 用于储存配置. 我们修改的配置项也将储存在这个数据库内. 生成了数据库后, 就可以用 Ctrl-C 结束运行了.

2026/07/16 17:42:56 No config file used
2026/07/16 17:42:56 WARNING: filebrowser.db can't be found. Initialing in /tmp/filebrowser/
2026/07/16 17:42:56 Using database: /tmp/filebrowser/filebrowser.db
2026/07/16 17:42:56 Performing quick setup
2026/07/16 17:42:56 User 'admin' initialized with randomly generated password: yFgD8wKg0yS1YrGF
2026/07/16 17:42:56 Listening on 127.0.0.1:8080
^C2026/07/16 17:44:34 Got signal: interrupt
2026/07/16 17:44:34 Stopped serving new connections.
2026/07/16 17:44:34 Graceful shutdown complete.

输出的日志显示程序创建了一个默认管理员用户, ID 始终为 admin, 密码随机, 这里是 yFgD8wKg0yS1YrGF.

将应用打包为单个文件

将应用打包为单个文件#

为什么要将 Linux 的程序打包为单个文件? 众所周知, 程序可能有数个甚至几十上百个依赖库, 或者叫动态链接库. 这一设计是为了减小程序体积, 因为可以多个程序共用同一套功能相同的代码. 但在一些时候, 它帮得倒忙远比其优点多. 具体地说, 当你的程序:

  • 依赖数量庞大的动态链接库;
  • 部分依赖安装起来非常麻烦, 或只能从半公开渠道获取 (例如需要注册后下载);
  • 无法静态编译, 或部分库未提供静态版本;
  • 需要在极短的时间内完成部署.

那么将其打包为单个文件可以解决你的烦恼.

当然, 这一方法并非没有缺点. 我们都很清楚, 把所有依赖都打包进来会导致分发文件体积膨胀严重, 甚至文件大小翻百倍都是常事. 在我的测试中, 打包后的应用大小在 120MB 左右. 如果你能接受这一点, 那么我确信它很适合你.

我们使用的文件格式被称为 AppImage, 对于几乎所有发行版, 它都是开箱即用的. 我们将先介绍手动打包的方式, 并以此认识包的格式; 随后给出更简单的打包方法.

手动打包#

下面的内容基于官方给出的 手动打包流程.

要手动打包, 你需要一个以下结构的目录:

MyApp.AppDir
├── AppRun
├── myapp.desktop
├── myapp.png
└── usr
     ├── bin
     │   └── myapp
     └── lib
         └── lib*.so

上面给出的是最小结构, 也就是说你不能再省略内容了. 目录名 *.AppDir 是惯例; AppRun 将作为被执行的程序, 它可以是可执行文件, 或是一个脚本, 关于它我们会在后文详细讲述; myapp.desktop 是清单文件, 文件名基本是任意的, 但根目录必须存在且只存在一个 .desktop 文件; myapp.png 是程序图标, 遗憾的是它不能省略. usr 目录就像 LFS 描述的一样, bin 内存放可执行文件, lib 内存放库文件. 你要执行的应用文件应该就是 myapp.

异步非阻塞串口数据接收

异步非阻塞串口数据接收#

异步?#

异步 (asynchronous) 编程是一种并发的编程模式, 可以有效节省 CPU 阻塞等待 I/O 事件造成的时间浪费, 借以提高性能.

在这里也简单介绍一下 并行 (parallel)并发 (concurrent) 的区别. 并发是指多个任务可以被同时运行的现象; 但需要注意的是, 并发在某一时刻可能并不一定有多个任务在运行. 但是并行是严格的多线程工作模式, 在某一时刻有多个任务在同时工作. 于是很容易看出, 并行也被包含在并发之中.

异步的核心在于一个字 “”. 简而言之, 异步编程好似去食堂窗口买饭, 当你告诉他你要点的餐以后, 不是一定要在窗口前等着, 而是可以去做点别的事情, 比如买瓶饮料什么的, 直到老板通知你取餐, 这就是一次异步事件. 在编程时我们调用一个异步 API, 若它不能立刻返回结果, 那么也不会阻塞等待, 而是立刻向后执行; 直到获取到结果再通知我们进行处理.

不同的语言有各色的异步支持, JavaScriptRust 的异步模型都很有代表性. JavaScript 中, 异步函数可以返回一个 Promise 对象, 其名字形象地说明了它的功能: 我承诺完成你要的操作; 潜台词则是我保证 将来 会完成你要的操作, 不一定是立刻做到. RustFuture 机制则更为直白: 你的任务我将来会完成的.

非多线程的异步程序的本质是 CPU 的 时分复用, 它用划分时间片交替运行各项任务来模拟同时运行每项任务. 必须指出, 有些语言实际上使用多线程实现的异步, 在这种情况下可以实现真正的并行, 即每个时刻多项任务同时被运行. 于是很容易推断, 若 CPU 本来就没有空闲时间, 那么非多线程的异步也并不能提高效率. 这也是为什么异步大多都被用于 I/O 操作, 因为可以有效利用好 I/O 事件中因为等待而空闲的 CPU 时间. 若是 CPU 密集型的工作, 例如科学计算等, 使用异步模型反而会因为时间片切换而损失性能. 这种情况更适合用多线程的方法提高效率.

在移动介质中安装操作系统

在移动介质中安装操作系统#

所需工具:

  • 安装了任意 linux 发行版的电脑
  • 此处使用安装了 debian 的 VMware 虚拟机
  • 系统安装光盘文件 (.iso 文件)
  • 我选择的发行版是 debian
  • 容量足够的移动介质 (如 U 盘)

1. 检查移动介质 (如 U 盘) 的分区表类型#

我们计划使用更现代的 UEFI 引导操作系统启动, 为了具有更好的兼容性, 移动介质的分区表类型应为 GPT.

因此第一步是将 MBR 类型的移动介质转换为 GPT 分区表类型. 假如你的介质已为 GPT, 可以跳过此章.

1.1. 检查分区表类型#

使用 fdisk 命令检查介质:

# fdisk -l
Disk /dev/sdb: 15.24 GiB, 16358834176 bytes, 31950848 sectors
Disk model: SD/MMC
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00109879

上面显示了在我的机器上返回的信息, 其中: