【问题标题】:Errno::EACCES upon deleting a file (on Windows 10)Errno::EACCES 删除文件时(在 Windows 10 上)
【发布时间】:2020-11-09 17:30:24
【问题描述】:

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

我在 Windows 10 上运行 Rails 6.0.3.2Ruby 2.6.6SQLite3版本 2004 (19041.388)。我按照 Rails 官方网站上的Getting Started guide 安装了 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) 是罪魁祸首。


尝试的解决方案

  • 对于 Windows,我能找到的唯一可行的答案是 this, 它主张添加一个“lib”gem,但它根本不起作用。

  • 我也尝试将文件模式从“w”更改为“wb+”,但没有 也可以工作。

  • 编辑 2: 根据 Dave Newton 在 cmets 中的建议(如果他是这个意思),我将图像存储目录移到了“app”文件夹之外;到“公共/上传/封面”。也没有用。

  • 编辑 3: 我将删除代码完全复制到另一个目录中的新脚本中,并在示例文件上进行了尝试。我得到了同样的错误。换句话说,问题不在于 Rails,而在于 Ruby(或我的操作系统)。

  • 我在终端上的文件上调用了rm,效果很好,所以我不知道这是否是文件权限问题。


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

【问题讨论】:

  • 你是如何运行它的?请注意,您应该从应用本身删除文件应用内:出于各种原因,文件应存储在应用外部。
  • @DaveNewton 我不太清楚你的问题是什么意思?至于存储:我不知道。我可以使用不同的目录没问题,但是当我将它托管到 Heroku(也是新的)时呢?那里除了根文件夹之外还有其他东西吗?我构建这个项目只是为了练习,所以我不需要 S3 存储或类似的东西。
  • @DaveNewton “请注意,您不应从应用程序本身删除应用程序内的文件:出于各种原因,文件应存储在应用程序外部。” ——等等,你的意思是“app”目录吗?我记得我为上传文件而复制的代码将它们保存在“公共”文件夹中。
  • 我的意思是“你是如何运行它的”。通常,网络应用程序没有能力(或理由)删除其自身层次结构中的文件。
  • @DaveNewton 这一切都来自开发版本(或任何你称之为 development.rb 的东西)。我想做的是列出“待售”书籍,每本书都有封面。我将这些封面图片保存在assets/images/covers/ 中,并在图书对象被销毁时尝试删除它们。

标签: ruby-on-rails ruby windows io ruby-on-rails-6


【解决方案1】:

与大多数 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 被忽略。

有各种可用标志的some documentation

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

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

【讨论】:

  • 我实际上不需要对文件做任何事情,所以我会坚持使用File.delete()。该语法是我在查找使用 Ruby 删除文件时发现的类似代码的残余。但对其他人来说是个好答案!
【解决方案2】:

把代码改成这个...

begin
    File.delete(book.cover_url)
rescue Errno::ENOENT
end

...现在可以使用了。不打开任何文件;只是 File.delete(URL)。


如果有人知道为什么原始版本不起作用,或者为什么它如此普遍地被建议,请发布答案或对此答案发表评论。

【讨论】:

  • 无法删除打开的文件。从来没有在任何地方看到过它的建议;这是荒谬的。
  • @DaveNewton 例如:stackoverflow.com/a/25270245/7151327。编辑:我应该在回答之前先看问题......
  • 可能。加上对答案的评论提出了您所看到的确切问题——这是一个糟糕的答案,因为它解决了一个问题并导致另一个问题。另一个答案是正确的。我要补充一点,这是一个自我回答的问题,花几分钟查看 Ruby 文档或搜索“Ruby 删除文件”,而不是简单地选择第一个结果的代码而不理解它:)
  • @DaveNewton 在我的辩护中,我在很多地方都看到了这种语法,但我没有仔细检查它们,所以直到后来我才想到要改变它。但是,是的,这不是我最自豪的时刻。
  • @DaveNewton:至少在 Unix 中,创建并打开一个临时文件,然后立即删除它是完全常见的。它将从公共命名空间中删除该文件,但仍允许任何具有打开文件句柄的进程使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-09
  • 2011-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
相关资源
最近更新 更多