首页 > 解决方案 > 在写入百万行 csv 文件时计算 md5 - 无需将其读入内存

问题描述

计算 md5 需要一个字节流才能通过。我假设在写入一百万行时可以将 csv.writer 作为字节流拦截。在下面的 py 代码中,写入了一百万行,我如何计算 md5 而无需仅为 md5 将文件读入内存?

def query2csv(connection, fileUri, sqlQuery, args):
    import csv
    tocsvfile = open(fileUri, 'w+')
    writer = csv.writer(tocsvfile, delimiter=',', quotechar='"') # , quoting=csv.QUOTE_MINIMAL
    #As a huge blob goes into writer, pass through, md5 how?
    # I do not want to read the huge file through memory just to compute md5
    with connection.cursor() as cur:
        cur.execute(sqlQuery, args)
        column_names = list(map(lambda x: x[0], cur.description))
        writer.writerow(column_names)
        writer.writerows(__batch_rows(cur))

标签: python-3.xstreammd5

解决方案


来自csv.writer的文档(强调我的):

csv.writer(csvfile, dialect='excel', **fmtparams)

返回一个编写器对象,该对象负责将用户的数据转换为给定类文件对象上的分隔字符串。csvfile可以是任何带有write()方法的对象。如果 csvfile 是一个文件对象,它应该用newline=''.

因此,我们可以拦截对 的调用.write(),并将数据输入 MD5 流,同时将其传递给真实文件。最简洁的方法是定义一个类,其write方法只调用一些函数(即一个用于 MD5 流,一个用于文件对象):

import csv
import hashlib

class WriterTee:
    def __init__(self, *outs):
        self.outs = outs

    def write(self, s):
        for f in self.outs:
            f(s)

def query2csv(connection, fileUri, sqlQuery, args):
    md5 = hashlib.md5()

    with open(fileUri, 'w+', newline='') as tocsvfile, connection.cursor() as cur:
        tee = WriterTee(
            tocsvfile.write,
            lambda s: md5.update(s.encode())
        )

        writer = csv.writer(tee, delimiter=',', quotechar='"')

        cur.execute(sqlQuery, args)
        column_names = list(map(lambda x: x[0], cur.description))
        writer.writerow(column_names)
        writer.writerows(__batch_rows(cur))

    return md5.hexdigest()

我进行了其他一些更改,以管理with块中的两种资源,并newline=''按照文档所说的那样使用。


顺便说一句,如果您可以选择,我建议您不要出于任何目的使用 MD5。MD5 并不安全,密码学家从 1996 年就开始反对它。即使您不认为安全属性与您的应用程序相关,使用安全散列算法也没有缺点,而且hashlib无论您选择哪种算法,API 都是相同的。


推荐阅读