首页 > 解决方案 > Errno::EACCES 删除文件时(在 Windows 10 上)

问题描述

有许多类似的问题,但没有一个答案对我有用。

我在Windows 10版本2004 (19041.388)上运行Rails 6.0.3.2Ruby 2.6.6SQLite3。我按照 Rails 官方网站上的入门指南安装了 Ruby on Rails,并且一切都应该是最新的。

我可以正常删除文件,并且我使用管理员帐户登录 - 不是必须的。

我是 Ruby 和 Rails 的新手,因此将不胜感激。


代码

以下是导致错误的原因:

def destroy
    book = Book.find(params[:id])
    
    begin
        File.open(book.cover_url, 'w') do |f|
            File.delete(f)
        end
    rescue Errno::ENOENT
    end

    book.destroy
    redirect_to books_path
end

这是做什么的,它首先删除一本书的封面图像,然后从数据库中删除这本书本身。


错误

错误画面:

错误屏幕截图

如果图片未加载,以下是错误消息:

Errno::EACCES in BooksController#destroy

Permission denied @ apply2files - D:/Projects/Web/RoR/ecommerce/app/assets/images/covers/circles_scaling_anim_positioning.png

File.delete(f)是罪魁祸首。


尝试的解决方案


编辑:我检查了有问题的文件,虽然它仍然存在,但它现在是 0 字节大,所以我认为它被空数据覆盖。然而,应该执行的其余代码destroy——即数据库中对象的销毁——似乎没有运行,因为对象仍然在那里。

标签: ruby-on-railsrubywindowsioruby-on-rails-6

解决方案


与大多数 UNIXy 操作系统(例如 macOS、Linux 或各种 BSD)相比,操作系统强制执行的文件处理在 Windows 上是不同的。

在 UNIXy 操作系统上,文件系统中的文件条目只是指向存储在磁盘上的“真实”文件的指针。还有其他可能的指针,例如文件被进程打开时的文件句柄,或硬链接(即指向完全相同文件的不同文件系统条目)。只要至少有一个指向该文件的有效指针,该文件就存在于磁盘上。

因此,在 UNIXy 系统上,您可以在从进程打开文件时从文件系统中删除(或重命名/移动)文件。文件本身只有在最后一个文件句柄关闭后才会真正被删除。

但是默认情况下,Windows 在这方面更加严格。只要有任何进程在文件上具有文件句柄(即如果任何进程打开了文件),它就不允许删除文件。这解释了为什么当你有文件句柄时不能删除文件

话虽如此,自从 Ruby 2.3.0 以来,您可以在打开文件时设置一个标志,指示 Windows 允许在打开文件时删除(或移动)文件:

# the flags for the normal 'w' mode
file_mode = IO::WRONLY | IO::CREAT | IO::TRUNC

# Add flags to allow deleting (or moving) the opened file
file_mode |= IO::BINARY | IO::SHARE_DELETE

File.open(book.cover_url, mode: file_mode) do |f|
  File.delete(f)
end

请注意,该IO::SHARE_DELETE标志仅适用于以二进制模式(而不是文本模式)打开的文件,在处理此打开的文件时要考虑这一点。在 UNIXy 系统上IO::SHARE_DELETE被忽略。

一些可用的各种标志的文档。

最后一点:我假设您的代码是一个缩短的示例,除了删除它之外,您还遗漏了一些实际与打开的文件交互的代码。

如果您只想删除文件,则无需先打开它。只需删除它File.delete(book.cover_url)。或者,如果您不关心任何错误(例如,如果文件首先不存在,您也可以使用FileUtils.rm_f(book.cover_url).


推荐阅读