Errno::EACCES 删除文件时(Windows 10)
Errno::EACCES upon deleting a file (on Windows 10)
有很多类似的问题,但 none 的答案对我有用。
我运行宁Rails6.0.3.2,Ruby2.6.6,以及 SQLite3 Windows 10 版本 2004 (19041.388)。我按照官方 Rails 站点上的 Getting Started guide 在 Rails 上安装了 Ruby,一切都应该是最新的。
我可以正常删除文件,而且我是用管理员帐户登录的——不是必须的。
我是 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 在评论中的建议(如果这是他的意思),我将图像存储目录移到了 'app'文件夹;到 'public/uploads/covers'。也没用。
编辑 3: 我将删除代码完全复制到另一个目录中的新脚本,并在示例文件上进行了尝试。我得到了同样的错误。换句话说,问题不在于 Rails,而在于 Ruby(或我的 OS)。
我从终端对文件调用了 rm
并且工作正常,所以我不知道这是否是文件权限问题。
编辑: 我检查了有问题的文件,虽然它仍然存在,但现在是 0 字节大,所以我认为它被空数据覆盖了。然而,应该在 destroy
上执行的其余代码——即数据库中对象的破坏——似乎没有 运行,因为该对象仍然在那里。
将代码更改为...
begin
File.delete(book.cover_url)
rescue Errno::ENOENT
end
...现在可以使用了。不打开任何文件;只是 File.delete(URL).
如果有人知道为什么原始版本不起作用,或者为什么它如此普遍被推荐,请post回答或对此回答发表评论。
与大多数 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)
.
有很多类似的问题,但 none 的答案对我有用。
我运行宁Rails6.0.3.2,Ruby2.6.6,以及 SQLite3 Windows 10 版本 2004 (19041.388)。我按照官方 Rails 站点上的 Getting Started guide 在 Rails 上安装了 Ruby,一切都应该是最新的。
我可以正常删除文件,而且我是用管理员帐户登录的——不是必须的。
我是 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 在评论中的建议(如果这是他的意思),我将图像存储目录移到了 'app'文件夹;到 'public/uploads/covers'。也没用。
编辑 3: 我将删除代码完全复制到另一个目录中的新脚本,并在示例文件上进行了尝试。我得到了同样的错误。换句话说,问题不在于 Rails,而在于 Ruby(或我的 OS)。
我从终端对文件调用了
rm
并且工作正常,所以我不知道这是否是文件权限问题。
编辑: 我检查了有问题的文件,虽然它仍然存在,但现在是 0 字节大,所以我认为它被空数据覆盖了。然而,应该在 destroy
上执行的其余代码——即数据库中对象的破坏——似乎没有 运行,因为该对象仍然在那里。
将代码更改为...
begin
File.delete(book.cover_url)
rescue Errno::ENOENT
end
...现在可以使用了。不打开任何文件;只是 File.delete(URL).
如果有人知道为什么原始版本不起作用,或者为什么它如此普遍被推荐,请post回答或对此回答发表评论。
与大多数 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)
.