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

时间:2013-09-06 19:22:50

标签: python python-2.7 file-io

我有一个自制的基于Web的文件系统,允许用户将文件下载为拉链;但是,我在生产系统上没有出现在我当地的盒子上时发现了一个问题。

在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 {{1}后添加+ }} 即可。

如果我将代码更改为'rb+',一切都按预期工作,代码成功擦除整个二进制文件并对其进行解密。如果我不添加加号则失败并且无法读取整个文件...

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

file = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb+')

澄清

密码错误可能是因为文件未完整读取。例如,我有一个500MB的文件,我一次只能读取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() 个字节。我读了直到我没有收到更多的字节,当我没有在windows中指定64*1024时,它循环遍历循环两次并返回一些蹩脚的数据(因为python认为它与字符串文件而不是二进制文件交互)。

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

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

1 个答案:

答案 0 :(得分:1)

我打算在这里猜一下:

你还有一些其他程序会不断替换你想要阅读的文件。

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

在Windows上,没有原子替换这样的东西。有多种方法可以解决这个问题,但很多人所做的就是就地重写文件。所以,当你打开一个文件时,你从8秒前得到版本,开始read它......然后突然有人将文件空白以重写它。 会影响您的文件句柄,因为它们已经重写了相同的文件。所以你打了一个EOF。

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

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


无论如何,如果你的问题有任何变化,你需要修复其他程序,重写文件,或者可能两个程序合作,以正确模拟Windows上的原子文件替换。只有这个程序才能解决它。*


*嗯,你可以做一些事情,比如乐观的检查 - 读取检查,并在modtime意外改变时重新开始,或者使用文件系统通知API,或者......但它会比将它固定在正确的位置。