繁体   English   中英

打开并编写一个大型二进制文件python

[英]opening and writing a large binary file python

我有一个基于Web的自制程序文件系统,允许用户下载zip文件。 但是,在生产系统上不存在的本地机器上进行开发时,发现一个问题。

在linux中,这不是问题(本地dev框是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所以它永远不会完全读到文件末尾... 除非我在'rb+'之后添加'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相同的行为,即仅从b中读取几个段关闭句柄并继续操作之前的文件,我最终得到的文件不完整,解密失败。

我将在这里猜测:

您还有其他程序会不断替换您要读取的文件。

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

在Windows上,没有原子替换之类的东西。 解决此问题的方法有很多种,但是许多人所做的只是就地重写文件。 因此,当您打开文件时,您会在8秒钟前获得该版本,开始read该文件……然后突然有人将文件清空以进行重写。 确实会影响您的文件句柄,因为它们已经重写了相同的文件。 因此,您命中了EOF。

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

实际上,这可能比这更微妙和令人讨厌。 Windows的更高版本尝试变得更聪明。 如果我尝试在别人锁定文件的同时打开文件,而不是立即失败,它可能会等待一小段时间然后重试。 确切的工作方式规则取决于您所需的共享和访问,并且在任何地方都没有真正记录在案。 有效地,只要它能按您希望的方式工作,就意味着您依赖种族条件。 这对于交互式的东西是很好的,例如将文件从资源管理器拖到记事本(最好是在99%的时间而不是10%的时间成功),但是对于试图可靠地工作的代码(在99%的时候成功)显然不可接受只是意味着问题很难调试)。 因此,由于您永远无法完全弄清它们的原因,并且如果您能够……也不愿依靠它,它可以轻松地在rr+模式之间以不同的方式工作。


无论如何,如果这是您的问题的任何变体,则需要修复该其他程序(重写文件的程序),或者可能需要两个程序配合使用,以在Windows上正确模拟原子文件替换。 仅此程序就无法解决该问题。*


*好吧,您可以执行乐观的检查-读取检查,并在modtime意外更改时重新开始,或者使用文件系统通知API,或者……但是,要比在正确的位置修复它要复杂得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM