You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:04:52