【问题标题】:opening and writing a large binary file python打开和编写一个大的二进制文件python
【发布时间】:2023-03-11 04:08:01
【问题描述】:

我有一个基于网络的自制文件系统,允许用户以 zip 格式下载文件;但是,我在生产系统上不存在的本地盒子上进行开发时发现了一个问题。

在 linux 中,这不是问题(本地开发盒是 windows 系统)。

我有以下代码

algo = CipherType('AES-256', 'CBC')
decrypt = DecryptCipher(algo, cur_share.key[:32], cur_share.key[-16:])
file = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb')
temp_file = open(temp_file_path, 'wb+')

data = file.read(settings.READ_SIZE)
while data:
    dec_data = decrypt.update(data)
    temp_file.write(dec_data)
    data = file.read(settings.READ_SIZE)

# Takes a dump right here!
# error in cipher operation (wrong final block length)
final_data = decrypt.finish()
temp_file.write(final_data)
file.close()
temp_file.close()

上面的代码打开一个文件,并(使用当前文件共享的密钥)解密文件并将其写入一个临时位置(稍后将被填充到一个 zip 文件中)。

我的问题在file = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb') 行。如果我不指定'rb',Windows 会非常关心二进制文件,因此文件将不会在数据读取循环中结束;但是,由于某种原因,由于我也在写信给temp_file,它永远不会完全读取到文件的末尾...除非我在 b 'rb+' 之后添加一个 +强>。

如果我将代码更改为file = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb+'),一切都按预期工作,并且代码成功地抓取了整个二进制文件并对其进行解密。如果我不添加加号,它会失败并且无法读取整个文件......

代码的另一部分(用于下载单个文件)读取(无论操作系统如何都可以完美运行):

algo = CipherType('AES-256', 'CBC')
decrypt = DecryptCipher(algo, cur_share.key[:32], cur_share.key[-16:])
file = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb')

filename = smart_str(cur_file.name, errors='replace')
response = HttpResponse(mimetype='application/octet-stream')
response['Content-Disposition'] = 'attachment; filename="' + filename + '"'

data = file.read(settings.READ_SIZE)
while data:
    dec_data = decrypt.update(data)
    response.write(dec_data)
    data = file.read(settings.READ_SIZE)

# no dumps to be taken when finishing up the decrypt process... 
final_data = decrypt.finish()
temp_file.write(final_data)
file.close()
temp_file.close()

澄清

密码错误可能是因为未完整读取文件。例如,我有一个 500MB 的文件,每次读取 64*1024 个字节。我一直读到我没有收到更多字节,当我没有在 Windows 中指定 b 时,它会循环两次并返回一些糟糕的数据(因为 python 认为它正在与字符串文件而不是二进制文件交互)。

当我指定 b 时,需要 10-15 秒才能完全读取文件,但它会成功完成,并且代码正常完成。

当我从源文件读入时同时写入另一个文件时(如第一个示例所示),如果我没有指定 rb+,它会显示与甚至没有指定 b 相同的行为,即在关闭句柄并继续之前,它只从文件中读取几个片段,我最终得到一个不完整的文件并且解密失败。

【问题讨论】:

  • “在这里转储”、“不会读到结束”等是什么意思?究竟会发生什么?
  • @abarnert 澄清添加。 - 需要注意的是,我知道添加+ 似乎可以解决这个问题,但我想知道它为什么会在 Windows 上运行以及打开文件句柄时发生了什么。这方面的python文档很薄弱。
  • 在另一段代码中没有decrypt = DecryptCipher(algo, cur_share.key[:32], cur_share.key[-16:]),所以它还在使用第一段的decrypt吗?
  • @mart 它正在创建另一个解密对象,但这对于手头的问题并不重要。密码问题是python没有正确读取文件内容的结果,除非我指定模式'rb+',其中'rb'就足够了......
  • @MikeMcMahon:我对你的澄清感到困惑。你说没有b 它循环循环两次,然后你说使用b 而不是+ 它与没有b 做同样的事情,然后你说它读取了几个片段然后关闭手柄。这是矛盾的。两者都没有任何意义。 rrb 之间的唯一区别是 r 将转换行尾(因此每对匹配 0D 0A 的字节将被单个字节 0A 替换)。

标签: python python-2.7 file-io


【解决方案1】:

我在这里猜测一下:

您有一些其他程序不断替换您尝试读取的文件。

在 linux 上,这个其他程序通过原子替换文件(即写入临时文件,然后将临时文件移动到路径)来工作。所以,当你打开一个文件时,你会得到 8 秒前的版本。几秒钟后,有人过来将其从目录中取消链接,但这不会以任何方式影响您的文件句柄,因此您可以在闲暇时read 整个文件。

在 Windows 上,没有原子替换之类的东西。有多种方法可以解决该问题,但许多人所做的只是就地重写文件。所以,当你打开一个文件时,你会得到 8 秒前的版本,开始 reading 它……然后突然有人将文件清空以重写它。这确实会影响您的文件句柄,因为它们重写了同一个文件。所以你遇到了 EOF。

r+ 模式下打开文件并不能解决问题,但它会添加一个隐藏它的新问题:您正在使用共享设置打开文件,阻止其他程序重写文件。所以,现在 other 程序失败了,这意味着没有人干扰这个程序,这意味着这个程序似乎可以工作。

事实上,它可能比这更微妙和烦人。更高版本的 Windows 试图变得聪明。如果我在其他人锁定文件时尝试打开文件,而不是立即失败,它可能会等待一小段时间然后重试。具体如何工作的规则取决于您需要的共享和访问,并且在任何地方都没有真正记录。实际上,只要它以您想要的方式工作,就意味着您依赖于竞争条件。这对于诸如将文件从资源管理器拖动到记事本之类的交互式内容很好(最好在 99% 的时间而不是 10% 的时间成功),但对于试图可靠工作的代码显然是不可接受的(在 99% 的时间成功只是意味着问题更难调试)。因此,它很容易在 rr+ 模式之间以不同的方式工作,因为您永远无法完全弄清楚,并且如果可以的话也不想依赖......


无论如何,如果这是您的问题,您需要修复其他程序,即重写文件的程序,或者可能两个程序合作,以正确模拟 Windows 上的原子文件替换。仅此程序无法解决此问题。*


* 好吧,您可以执行诸如乐观检查-读取-检查之类的操作,并在 modtime 意外更改时重新开始,或者使用文件系统通知 API,或者……但它会比它复杂得多将其固定在正确的位置。

【讨论】:

  • 我将使用进程资源管理器工具进行调查,但我找不到任何改变文件(或不断写入文件)的东西..
  • 虽然我找不到竞争条件,但您确实最好地解释了 r+brb 之间的区别以及为什么它导致文件的不同处理(锁定它与共享)。在这一点上,这主要是一个微不足道的问题,因为我似乎找不到可能会触及它的东西(因为没有什么应该),并且因为我们主要在 *nix 环境中运行它,其中(如您所说)它确实支持原子文件操作.
  • @MikeMcMahon:如果它在本地驱动器上,FindFirstChangeNotification 是找到对文件的间歇性访问的最佳方法,并且有一些工具可以很好地包装它(我认为 sysinternals 有一个命令-线工具,它在他们的 GUI 工具之一中)。如果它在 SMB 股票上,那就完全是另一回事了,但希望你不必处理这个问题。
猜你喜欢
  • 2013-05-13
  • 2015-10-26
  • 2012-01-17
  • 2015-11-29
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-24
相关资源
最近更新 更多