XlsxWriter: Почему in_memory дает поврежденную последовательность байтов? - PullRequest
1 голос
/ 10 марта 2020

Я создаю файл Excel и возвращаю его для загрузки в приложении flask. Если я использую 'in_memory': True, то файл поврежден и Excel не может его открыть. Тот же код, если записан непосредственно на диск, работает. Для кода в памяти я следую этому примеру из документов XlsxWriter .

Я скептически относился к тому, что байты были разными, поэтому я написал небольшой скрипт для тестирования, и они делают немного отличаются (44 байта из 5730).

Вот мой скрипт, генерирующий идентичные книги, одну в памяти и одну на диске. Затем он сравнивает байты и обнаруживает, что они разные. Почему?

from io import BytesIO

from xlsxwriter import Workbook


def fill_workbook(workbook):
    """Populate the workbook with some test data"""
    first_sheet = workbook.add_worksheet("First")
    first_sheet.write(0, 0, "test")

    next_sheet = workbook.add_worksheet("Next")
    next_sheet.write(0, 0, "sample")
    next_sheet.write(0, 1, "value")
    workbook.close()


def get_bytes():
    """Get the bytes for the in-memory and on-disk workbooks"""
    output = BytesIO()
    in_mem = Workbook(output, {'in_memory': True})

    filename = "direct.xlsx"
    on_disk = Workbook(filename)

    fill_workbook(in_mem)
    fill_workbook(on_disk)

    output.seek(0)
    mem_bytes = output.read()

    with open(filename, "rb") as f:
        disk_bytes = f.read()

    return mem_bytes, disk_bytes


def compare_bytes():
    """Compare the bytes of the two workbooks"""
    mem_bytes, disk_bytes = get_bytes()

    print(mem_bytes == disk_bytes)

    same = 0
    diff = 0
    for mb, db in zip(mem_bytes, disk_bytes):
        if mb == db:
            same += 1
        else:
            diff +=1

    print(f"{same} bytes same")
    print(f"{diff} bytes different")


if __name__ == '__main__':
    compare_bytes()

Я запустил свой скрипт на Python 3.7.3 с XlsxWriter==1.2.8

1 Ответ

1 голос
/ 11 марта 2020

Это работает для меня, когда in_memory установлен на False. Давайте посмотрим, что на самом деле делает аргумент in_memory в исходном коде XlsxWriter:

workbook.py на Github

    for file_id, file_data in enumerate(xml_files):
        os_filename, xml_filename, is_binary = file_data

        if self.in_memory:

            # Set sub-file timestamp to Excel's timestamp of 1/1/1980.
            zipinfo = ZipInfo(xml_filename, (1980, 1, 1, 0, 0, 0))

            # Copy compression type from parent ZipFile.
            zipinfo.compress_type = xlsx_file.compression

            if is_binary:
                xlsx_file.writestr(zipinfo, os_filename.getvalue())
            else:
                xlsx_file.writestr(zipinfo,
                                   os_filename.getvalue().encode('utf-8'))`

        else:
            # The sub-files are tempfiles on disk, i.e, not in memory.

            # Set sub-file timestamp to 31/1/1980 due to portability
            # issues setting it to Excel's timestamp of 1/1/1980.
            timestamp = time.mktime((1980, 1, 31, 0, 0, 0, 0, 0, -1))
            os.utime(os_filename, (timestamp, timestamp))

            try:
                xlsx_file.write(os_filename, xml_filename)
                os.remove(os_filename)
...