C#文件上传病毒扫描 C#如何集成ClamAV等工具扫描上传的文件

来源:这里教程网 时间:2026-02-21 17:42:05 作者:

ClamAV 服务端部署是前提,别直接在 C# 进程里调用 clamdscan

ClamAV 不是纯 .NET 库,不能通过

NuGet
引入后直接
ScanFile()
。它依赖本地运行的
clamd
守护进程(Linux/macOS)或 Windows 服务(
clamd.exe
),C# 只能走 socket 或命令行与其通信。跳过服务部署直接写 C# 调用,99% 会卡在连接拒绝或权限错误。

实操建议:

Linux:用包管理器安装
clamav-daemon
,确认
clamd
正在监听
/var/run/clamav/clamd.ctl
127.0.0.1:3310
Windows:下载官方
clamav-x.x.x-win-x64.zip
,运行
clamd.exe --install
后启动服务,检查端口
3310
是否开放
别用
clamscan
命令行模式做生产扫描——每次调用都 fork 新进程,上传并发高时 CPU 和文件句柄直接打满

C# 调用 clamd 的推荐方式:TCP socket + Unix domain socket 兼容封装

clamd
协议极简(文本协议),自己写 socket 比引入第三方库更可控、更轻量。关键不是“怎么连”,而是“怎么防粘包+怎么判响应结束”。官方协议要求每条命令以
 结尾,响应以 
STREAM
开头表示流式扫描,以
OK
FOUND
结尾。

实操建议:

TcpClient
连接
127.0.0.1:3310
(Windows/Linux TCP 模式),或
UnixDomainSocket
(.NET 6+ Linux/macOS 推荐,性能更好)
发送命令前必须先发
VERSION
确认服务可用,避免上传后才报连接失败
扫描大文件时,别一次性把整个
byte[]
写进 socket——改用
NetworkStream.WriteAsync()
分块推送,否则可能触发 clamd 的
StreamMaxLength
限制(默认 25MB)
响应解析不要用
ReadLine()
,而要按字节读取直到遇到
,再判断是否含 
FOUND
ERROR

上传文件落地前扫描,别扫
IFormFile.OpenReadStream()
返回的未缓冲流

ASP.NET Core 的

IFormFile
流默认是非缓冲、不可重入的。一旦你用
stream.CopyToAsync(clamdStream)
扫描一次,后续想保存到磁盘或计算哈希就会失败——流已 EOF 或被关闭。

实操建议:

必须先将上传文件临时写入磁盘(如
Path.GetTempFileName()
),再把该路径传给 clamd 的
SCAN
命令(注意:clamd 必须配置
AllowSupplementaryGroups yes
且运行用户有读权限)
或者用内存流双写:创建
MemoryStream
,用
file.CopyToAsync(memStream)
,然后分别用
memStream
推送扫描、用
memStream.Position = 0
再保存——但仅限小文件(
千万别在
Controller
里直接
await file.OpenReadStream().CopyToAsync(...)
后又想读一遍——.NET 不支持流回溯,除非显式
CanSeek == true
且底层是可寻址流

常见错误:SCAN 命令返回
ERROR: Can't open file
或超时

这不是 C# 代码问题,而是 clamd 权限或配置没对齐。尤其在容器或 Linux systemd 环境下,

clamd
默认以
clamav
用户运行,根本读不了你 ASP.NET 进程临时写的文件。

实操建议:

检查 clamd 日志:
tail -f /var/log/clamav/clamd.log
(Linux)或 Windows 事件查看器里
Application
日志,真实错误比 C# 报的
SocketException
有用得多
临时测试时,在 clamd.conf 中加
LocalSocketGroup www-data
(Ubuntu)或
LocalSocketMode 666
,并确保上传临时目录属组包含该组
超时不是网络问题,而是 clamd 的
StreamMaxLength
MaxFileSize
配置太小(默认 25MB/30MB),上传 100MB 视频必失败——需同步调大这两个值并重启服务
Windows 上若用命名管道(
LocalSocket \.pipeclamd
),.NET 的
NamedPipeClientStream
要设
useAsync = true
,否则阻塞主线程

真正麻烦的从来不是连上 clamd,而是让它的权限、路径、超时和你的上传生命周期严丝合缝。多看日志,少猜代码。

相关推荐