Ruby-inotify监听新扫描仪文件时出现多余波浪号~的问题求助
问题描述
我有一个Ruby脚本,用于监听指定路径,等待扫描后的PDF文档上传完成后执行后续处理与通知操作。服务器运行简单配置的vsftpd,原本使用佳能ImageRunner扫描仪通过FTP上传文件,脚本运行正常。
脚本代码如下:
require 'rb-inotify' notifier = INotify::Notifier.new notifier.watch("/root/test", :close_write) do |event| p event.absolute_name p event.name end notifier.run
正常情况下,脚本能正确获取文件名和完整路径:
"/root/test/doc1.pdf" "doc1.pdf" "/root/test/doc2.pdf" "doc2.pdf"
更换新扫描仪后,脚本触发事件返回的文件名末尾会多出一个波浪号~,导致后续处理异常:
"/root/test/doc1.pdf~" "doc1.pdf~" "/root/test/doc2.pdf~" "doc2.pdf~"
但实际查看文件夹时,文件的最终名称是正确的(无~),推测是新扫描仪的FTP写入方式导致——它可能先写入带~的临时文件,完成后再重命名为正式文件名。
尝试过用[0...-1]截断末尾~作为临时方案,但会破坏其他场景的功能(比如手动上传或其他扫描仪的正常文件名会被错误截断为doc1.pd),需要更安全的解决方法。
安全解决方案
以下几种方法可以针对性解决该问题,同时避免影响正常文件的处理:
1. 验证文件存在性,等待正式文件生成
由于带~的是临时文件,最终会被重命名为无~的正式文件,可以在触发事件后先检查无~的目标文件是否存在,只有存在时才处理:
require 'rb-inotify' notifier = INotify::Notifier.new # 同时监听写入完成和重命名完成事件 notifier.watch("/root/test", :close_write, :moved_to) do |event| # 只处理PDF相关文件 next unless event.name.match?(/\.pdf(~)?$/) target_path = if event.name.end_with?('~') event.absolute_name.chomp('~') else event.absolute_name end # 等待临时文件完成重命名,最多等待3秒(可根据实际调整) 3.times do if File.exist?(target_path) && !File.directory?(target_path) p target_path p File.basename(target_path) # 这里执行后续处理逻辑 break end sleep 1 end end notifier.run
2. 过滤临时文件,只处理最终正式文件
直接忽略带~的临时文件事件,只监听正式PDF文件的生成。由于扫描仪是先写临时文件再重命名,需要同时监听:moved_to事件(文件重命名完成时触发):
require 'rb-inotify' notifier = INotify::Notifier.new # 同时监听文件写入完成和移动/重命名完成事件 notifier.watch("/root/test", :close_write, :moved_to) do |event| # 跳过临时文件,只处理正式PDF next if event.name.end_with?('~') next unless event.name.end_with?('.pdf') p event.absolute_name p event.name # 执行后续处理 end notifier.run
3. 结合文件大小变化判断上传完成
部分FTP客户端会分块写入文件,临时文件的大小会持续变化,直到上传完成才会重命名。可以通过监控文件大小是否稳定来判断是否是最终文件:
require 'rb-inotify' def file_stable?(path, check_interval = 0.5, checks = 2) return false unless File.exist?(path) size = File.size(path) checks.times do sleep check_interval return false if File.size(path) != size end true end notifier = INotify::Notifier.new notifier.watch("/root/test", :close_write) do |event| file_path = event.absolute_name # 处理PDF临时文件或正式文件 if file_path.end_with?('.pdf~') target_path = file_path.chomp('~') # 等待临时文件完成重命名且正式文件稳定 if file_stable?(target_path) p target_path p File.basename(target_path) end elsif file_path.end_with?('.pdf') && file_stable?(file_path) p file_path p event.name end end notifier.run
4. 调整vsftpd配置(可选)
如果服务器权限允许,可以修改vsftpd的配置,尝试禁用临时文件写入模式:
- 打开vsftpd配置文件(通常是
/etc/vsftpd.conf) - 确保
write_enable=YES已配置 - 检查是否有
temp_file_name相关配置,如有可调整或注释掉 - 重启vsftpd服务:
systemctl restart vsftpd
注意:这种方法依赖扫描仪是否遵循FTP服务器的配置,不一定适用于所有设备。
内容的提问来源于stack exchange,提问作者user3788685
相关产品推荐
相关产品推荐

