【问题标题】:Delete folder after send_file in Rails在 Rails 中 send_file 之后删除文件夹
【发布时间】:2012-11-01 09:35:15
【问题描述】:

我正在使用 send_file 从我的 Rails 控制器发送文件 file.txt,然后删除包含它的文件夹。

send_file("#{Rails.root}/public/folder/file.txt")
FileUtils.remove_dir "#{Rails.root}/public/folder", true

当我这样做时,file.txt 被发送并删除。但是,folder 没有被删除。但是如果我删除send_file 行,那么folder 将被删除。

如何让它删除folder

编辑:有趣的是,我发现在folder 内部有一个名为.__afs2B0C 的隐藏文件,可能会阻止删除。我不知道这个文件是如何创建的!该文件在消失之前仅保留大约 15 分钟。

EDIT2:我尝试使用vi 检查临时文件的内容,但这是不可读的乱码。当我仅删除 send_file 行时,该文件夹已正确删除。当我仅删除 FileUtils.remove_dir 行时,该文件夹不包含临时文件。

【问题讨论】:

  • 如果您没有真正的理由使用此设置(例如,您希望文件夹保持错误状态),您可以考虑改用TempFile:这些文件在请求后会自动清除。
  • @nathanvda 感谢您的回答,但我不太明白。我应该在我的代码中具体在哪里使用Tempfile
  • 我假设您正在创建文件,因为您删除了它们。也许这是一个错误的假设。
  • @nathanvda 我明白了。我使用wget下载了一些文件然后压缩它们,所以使用Tempfile可能太复杂了。
  • 恕我直言,这非常适合使用TempFile,但压缩一个完整的文件夹可能更容易。

标签: ruby-on-rails sendfile


【解决方案1】:

您确定在删除目录时 send_file 仍然没有发送文件,如果它使用 X-SendFile 可能是异步的?尝试删除目录时会导致错误。

因此,您可能应该将此删除操作排队,或者稍后使用清扫器执行此操作,而不是在将文件发送到流媒体后立即尝试执行此操作。

我不完全清楚您发送的是哪个文件,因此在您的问题中包含文件路径和文件类型的实际示例以及它是如何创建的会很有用。

可能的调试帮助:

在执行以下操作时登录并监视文件夹:

  • 写出一个非常大的文件(> 60MB),并检查在您的文件创建过程中没有创建不可见的文件 - 我不清楚您实际发送的是哪个文件
  • 在慢速连接上设置大文件传输,并注意该文件的创建和可能的增长(例如,它可能与压缩动态提供的文件有关)。

鉴于当您尝试删除时 sendfile 可能仍会通过 Web 服务器发送(对于大文件)(x-send-file 现在是默认设置),我会尝试研究延迟解决方案。

可能的解决方案:

  • 使用 send_data 而不是 send_file(如果文件很小)
  • 使用延迟作业等安排删除文件夹以供以后使用
  • 设置一个清扫器,在每天结束时删除文件夹

【讨论】:

  • 文件已正确发送和删除,所以我认为send_file 不是仍在发送文件。即使我在send_file 之后输入sleep(10),也会观察到相同的结果。
  • 嗯,那么不确定。也许尝试取出 true 并查看 remove_dir 是否引发错误。
  • 如果我取出true,它会在目录public/folder 上引发错误Directory not empty。这说明什么?
  • 也许尝试在 send_file 之后直接在目录上执行 ls -la
  • 其实我刚刚发现确实有一个隐藏文件叫.__afs2B0C。我不知道这个文件是如何创建的!
【解决方案2】:

不知道为什么那个隐藏文件在那里,它可能是 X-send-file 甚至 wget 的一个分支(部分进度之类的)。

理想情况下,您应该使用 Tempfile 来执行此类操作。该代码基于您对您正在做的事情的评论。另外,我使用了两个 gem,一个用于下载,另一个用于压缩。 这样,您根本不需要创建文件夹,只需一个 zip 文件即可。 zip 的所有内容文件将自行删除。下载 zip 后,只需将其删除。在这里我还要提一下,您可能会在某处遇到故障,因为 send_file 会将传输移交给网络服务器,因此您不会在文件仍在提供服务时删除 rails 进程。所以即使这样,并且它在 localhost 上运行良好,我强烈建议在生产中使用自定义的预定后台垃圾收集器。

require 'open-uri'
require 'zip/zip'
zip_path = "#{Rails.root}/public/test.zip"
urls_to_fetch = ['abc.com', 'xyz.com']

Zip::ZipFile.open(zip_path, Zip::ZipFile::CREATE) do |zipfile|
    urls_to_fetch.each_with_index do |url, index|
        # intialize new temp file
        file = Tempfile.new(index.to_s)
        # fetch the file using open-uri or wget and save it as a tmpfile
        open(url, 'rb') do |read_file|
            file.write(read_file.read)
        end
    end
    # add the temp file to the list of files to zip
    zipfile.add(File.basename(file), file.path)
end
# send the zipfile for download
send_file zip_path
# delete the zipfile
FileUtils.rm zip_path

但是,这不应该是强制性的。如果你在没有 Tempfiles 的情况下做事,请检查 rails runner 对目标目录的权限。

FileUtils 文档详细介绍了尝试删除文件/文件夹时的本地安全漏洞。

【讨论】:

  • TempFile 无济于事,TempFile 对象在发送文件之前仍可能被垃圾收集(并尝试将其从磁盘中删除),明确的 remove_dir 是一样好。
  • 只有在该方法执行完毕后,TempFile 才会被垃圾回收。这不是问题,因为一旦将 TempFile 添加到 zipfile 中,它就会变得无用。 unlinkclose 在代码中的任何地方都没有被显式调用。
【解决方案3】:

这很简单,但很危险。使用shell命令来实现它。将其放在send_file 之后的Controller

system ("rm -rf public/folder")

【讨论】:

  • 您的“解决方案”不仅危险,而且实际上比FileUtils.remove_dir "#{Rails.root}/public/folder", true 更复杂。顺便说一句,OP 已经尝试过 rm -rf 方法,但也失败了——请参阅 stackoverflow.com/questions/13336209/…
【解决方案4】:

【讨论】:

    【解决方案5】:

    See here... works for me

     file = File.open(Rails.root.join('public', 'uploads', filename), "rb")
    contents = file.read
    file.close
    
    File.delete(filepath) if File.exist?(filepath)
    
    send_data(contents, :filename => filename)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-17
      • 1970-01-01
      • 2021-12-24
      • 2020-09-03
      • 1970-01-01
      • 2018-05-01
      • 2020-07-12
      • 1970-01-01
      相关资源
      最近更新 更多