zip-rs/zip-old

Large zip file cannot be read by Excel

jmcnamara opened this issue · 11 comments

I have a library call rust_xlsxwriter that uses zip.rs to create xlsx files.

The crate is reasonably well used and to date I haven't had any issues from Excel, either personally or from end users.

However, one user who is creating large (but not large_file large) 400MB files with lots of images reported an issue with the following file:

Bad file: basic-full.xlsx

Excel complains about this almost immediately when it tries to open it:

screenshot 1

I don't see any issues in the zipped XML data (the content) and if I unzip and rezip the file then Excel can open it:

unzip basic-full.xlsx -d basic-full

cd basic-full
find . -type f | xargs zip ../basic-rezipped.xlsx
cd ..

open basic-rezipped.xlsx

259741048-bbe3a64f-ac79-48aa-a7d3-af3027d14d0e

Good file: basic-rezip.xlsx

Also, a Microsoft Open XML SDK validation tool complains about the zip file when I try to open the bad file:

CleanShot 2023-08-11 at 21 14 43

So it looks to me like there is an issue with the zip container.

The bad file above was created with zip.rs v0.6.4 but I get the same result with v0.6.6. See the following for additional details: jmcnamara/rust_xlsxwriter#51 Also, I'm second guessing that the issue is with large files. It could be related to the number of files in the zip: 87814.

Thanks for taking a look at this and let me know if you need any more data.

Also, the file was created with the following zip.rs options:

    pub(crate) fn new(writer: W) -> Packager<W> {
        let zip = zip::ZipWriter::new(writer);

        let zip_options = FileOptions::default()
            .compression_method(zip::CompressionMethod::Deflated)
            .unix_permissions(0o600)
            .last_modified_time(DateTime::default())
            .large_file(false);

        Packager { zip, zip_options }
    }

And the following dependency in Cargo.toml:

zip = {version = "0.6.4", default-features = false, features = ["deflate"]}

Turning on the large_file ZIP64 option doesn't help.

Also, and I have no idea if this is helpful, but I get the same bad file result when I add the zlib feature to zip.rs.

Looking at the zipdetails -vv output of the two files, I see a few differences:

  • The repacked zip includes a couple extra Unix-related extra fields. I highly doubt this is what makes it work in Excel.

  • The "internal file attributes" field is set to binary (0) for all files in the original, while it's set to text (1) just for the text files in the repacked version. I also doubt this is the issue if it's only large files that break.

  • The zip64 EOCD (end of central directory) structure specifies the required zip spec version for extraction as 4.6 in the original, but the repacked one only specifies 4.5. A total shot in the dark given Excel's lack of any useful error messages, but I'm guessing this may be the issue. Zips with less than 65536 entries wouldn't have a zip64 EOCD, so that might be why small files work.

    The value is currently hardcoded in zip-rs. It might be worth trying to change https://github.com/zip-rs/zip/blob/21a20584bc9e05dfa4f3c5b0bc420a1389fae2c3/src/types.rs#L275 to 45 locally and see if that fixes the issue?

    I believe that should be safe to do without causing further issues. In section 4.4.3 of https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, it looks like version 4.6 only introduced support for bzip2-compressed entries and the sample files don't contain any of those.

EDIT: In case it's not easy to regenerate the file, you can modify the field by replacing 0x2e (46) at offset 0xCAA3A20 (212482592) with 0x2d (45). Eg:

#!/usr/bin/env python3

with open('temp.xlsx', 'rb+') as f:
    f.seek(0xCAA3A20)
    assert f.read(1) == b'\x2e'

    f.seek(0xCAA3A20)
    f.write(b'\x2d')

zipdetails outputs:

Local file header for [Content_Types].xml (original)
0000000 0000003 0000004 50 4B 03 04 LOCAL HEADER #1       04034B50 (67324752)
0000004 0000004 0000001 14          Extract Zip Spec      14 (20) '2.0'
0000005 0000005 0000001 00          Extract OS            00 (0) 'MS-DOS'
0000006 0000007 0000002 00 00       General Purpose Flag  0000 (0)
                                    [Bits 1-2]            0 'Normal Compression'
0000008 0000009 0000002 08 00       Compression Method    0008 (8) 'Deflated'
000000A 000000D 0000004 00 00 21 00 Last Mod Time         00210000 (2162688) 'Mon Dec 31 19:00:00 1979'
000000E 0000011 0000004 20 4A B5 45 CRC                   45B54A20 (1169508896)
0000012 0000015 0000004 8B 01 00 00 Compressed Length     0000018B (395)
0000016 0000019 0000004 39 0A 00 00 Uncompressed Length   00000A39 (2617)
000001A 000001B 0000002 13 00       Filename Length       0013 (19)
000001C 000001D 0000002 00 00       Extra Length          0000 (0)
000001E 0000030 0000013 5B 43 6F 6E Filename              '[Content_Types].xml'
                        74 65 6E 74
                        5F 54 79 70
                        65 73 5D 2E
                        78 6D 6C
0000031 00001BB 000018B ...         PAYLOAD
Local file header for [Content_Types].xml (repacked)
0000345 0000348 0000004 50 4B 03 04 LOCAL HEADER #3       04034B50 (67324752)
0000349 0000349 0000001 14          Extract Zip Spec      14 (20) '2.0'
000034A 000034A 0000001 00          Extract OS            00 (0) 'MS-DOS'
000034B 000034C 0000002 00 00       General Purpose Flag  0000 (0)
                                    [Bits 1-2]            0 'Normal Compression'
000034D 000034E 0000002 08 00       Compression Method    0008 (8) 'Deflated'
000034F 0000352 0000004 00 00 21 00 Last Mod Time         00210000 (2162688) 'Mon Dec 31 19:00:00 1979'
0000353 0000356 0000004 20 4A B5 45 CRC                   45B54A20 (1169508896)
0000357 000035A 0000004 96 01 00 00 Compressed Length     00000196 (406)
000035B 000035E 0000004 39 0A 00 00 Uncompressed Length   00000A39 (2617)
000035F 0000360 0000002 13 00       Filename Length       0013 (19)
0000361 0000362 0000002 1C 00       Extra Length          001C (28)
0000363 0000375 0000013 5B 43 6F 6E Filename              '[Content_Types].xml'
                        74 65 6E 74
                        5F 54 79 70
                        65 73 5D 2E
                        78 6D 6C
0000376 0000377 0000002 55 54       Extra ID #1           5455 (21589) 'Extended Timestamp [UT]'
0000378 0000379 0000002 09 00         Length              0009 (9)
000037A 000037A 0000001 03            Flags               03 (3) 'mod access'
000037B 000037E 0000004 80 35 CE 12   Mod Time            12CE3580 (315504000) 'Mon Dec 31 11:00:00 1979'
000037F 0000382 0000004 80 35 CE 12   Access Time         12CE3580 (315504000) 'Mon Dec 31 11:00:00 1979'
0000383 0000384 0000002 75 78       Extra ID #2           7875 (30837) 'Unix Extra type 3 [ux]'
0000385 0000386 0000002 0B 00         Length              000B (11)
0000387 0000387 0000001 01            Version             01 (1)
0000388 0000388 0000001 04            UID Size            04 (4)
0000389 000038C 0000004 E8 03 00 00   UID                 000003E8 (1000)
000038D 000038D 0000001 04            GID Size            04 (4)
000038E 0000391 0000004 E8 03 00 00   GID                 000003E8 (1000)
0000392 0000527 0000196 ...         PAYLOAD
Central directory header for [Content_Types].xml (original)
C4DF06F C4DF072 0000004 50 4B 01 02 CENTRAL HEADER #1     02014B50 (33639248)
C4DF073 C4DF073 0000001 2E          Created Zip Spec      2E (46) '4.6'
C4DF074 C4DF074 0000001 03          Created OS            03 (3) 'Unix'
C4DF075 C4DF075 0000001 14          Extract Zip Spec      14 (20) '2.0'
C4DF076 C4DF076 0000001 00          Extract OS            00 (0) 'MS-DOS'
C4DF077 C4DF078 0000002 00 00       General Purpose Flag  0000 (0)
                                    [Bits 1-2]            0 'Normal Compression'
C4DF079 C4DF07A 0000002 08 00       Compression Method    0008 (8) 'Deflated'
C4DF07B C4DF07E 0000004 00 00 21 00 Last Mod Time         00210000 (2162688) 'Mon Dec 31 19:00:00 1979'
C4DF07F C4DF082 0000004 20 4A B5 45 CRC                   45B54A20 (1169508896)
C4DF083 C4DF086 0000004 8B 01 00 00 Compressed Length     0000018B (395)
C4DF087 C4DF08A 0000004 39 0A 00 00 Uncompressed Length   00000A39 (2617)
C4DF08B C4DF08C 0000002 13 00       Filename Length       0013 (19)
C4DF08D C4DF08E 0000002 00 00       Extra Length          0000 (0)
C4DF08F C4DF090 0000002 00 00       Comment Length        0000 (0)
C4DF091 C4DF092 0000002 00 00       Disk Start            0000 (0)
C4DF093 C4DF094 0000002 00 00       Int File Attributes   0000 (0)
                                    [Bit 0]               0 'Binary Data'
C4DF095 C4DF098 0000004 00 00 80 81 Ext File Attributes   81800000 (2172649472)
                                    [Bits 16-24]          0180 (384) 'Unix attrib: rw-------'
                                    [Bits 28-31]          08 (8) 'Regular File'
C4DF099 C4DF09C 0000004 00 00 00 00 Local Header Offset   00000000 (0)
C4DF09D C4DF0AF 0000013 5B 43 6F 6E Filename              '[Content_Types].xml'
                        74 65 6E 74
                        5F 54 79 70
                        65 73 5D 2E
                        78 6D 6C
Central directory header for [Content_Types].xml (repacked)
C78C6BB C78C6BE 0000004 50 4B 01 02 CENTRAL HEADER #3     02014B50 (33639248)
C78C6BF C78C6BF 0000001 1E          Created Zip Spec      1E (30) '3.0'
C78C6C0 C78C6C0 0000001 03          Created OS            03 (3) 'Unix'
C78C6C1 C78C6C1 0000001 14          Extract Zip Spec      14 (20) '2.0'
C78C6C2 C78C6C2 0000001 00          Extract OS            00 (0) 'MS-DOS'
C78C6C3 C78C6C4 0000002 00 00       General Purpose Flag  0000 (0)
                                    [Bits 1-2]            0 'Normal Compression'
C78C6C5 C78C6C6 0000002 08 00       Compression Method    0008 (8) 'Deflated'
C78C6C7 C78C6CA 0000004 00 00 21 00 Last Mod Time         00210000 (2162688) 'Mon Dec 31 19:00:00 1979'
C78C6CB C78C6CE 0000004 20 4A B5 45 CRC                   45B54A20 (1169508896)
C78C6CF C78C6D2 0000004 96 01 00 00 Compressed Length     00000196 (406)
C78C6D3 C78C6D6 0000004 39 0A 00 00 Uncompressed Length   00000A39 (2617)
C78C6D7 C78C6D8 0000002 13 00       Filename Length       0013 (19)
C78C6D9 C78C6DA 0000002 18 00       Extra Length          0018 (24)
C78C6DB C78C6DC 0000002 00 00       Comment Length        0000 (0)
C78C6DD C78C6DE 0000002 00 00       Disk Start            0000 (0)
C78C6DF C78C6E0 0000002 01 00       Int File Attributes   0001 (1)
                                    [Bit 0]               1 'Text Data'
C78C6E1 C78C6E4 0000004 00 00 80 81 Ext File Attributes   81800000 (2172649472)
                                    [Bits 16-24]          0180 (384) 'Unix attrib: rw-------'
                                    [Bits 28-31]          08 (8) 'Regular File'
C78C6E5 C78C6E8 0000004 45 03 00 00 Local Header Offset   00000345 (837)
C78C6E9 C78C6FB 0000013 5B 43 6F 6E Filename              '[Content_Types].xml'
                        74 65 6E 74
                        5F 54 79 70
                        65 73 5D 2E
                        78 6D 6C
C78C6FC C78C6FD 0000002 55 54       Extra ID #1           5455 (21589) 'Extended Timestamp [UT]'
C78C6FE C78C6FF 0000002 05 00         Length              0005 (5)
C78C700 C78C700 0000001 03            Flags               03 (3) 'mod access'
C78C701 C78C704 0000004 80 35 CE 12   Mod Time            12CE3580 (315504000) 'Mon Dec 31 11:00:00 1979'
C78C705 C78C706 0000002 75 78       Extra ID #2           7875 (30837) 'Unix Extra type 3 [ux]'
C78C707 C78C708 0000002 0B 00         Length              000B (11)
C78C709 C78C709 0000001 01            Version             01 (1)
C78C70A C78C70A 0000001 04            UID Size            04 (4)
C78C70B C78C70E 0000004 E8 03 00 00   UID                 000003E8 (1000)
C78C70F C78C70F 0000001 04            GID Size            04 (4)
C78C710 C78C713 0000004 E8 03 00 00   GID                 000003E8 (1000)
EOCD and EOCD locator (original)
CAA3A14 CAA3A17 0000004 50 4B 06 06 ZIP64 END CENTRAL DIR 06064B50 (101075792)
                                    RECORD
CAA3A18 CAA3A1F 0000008 2C 00 00 00 Size of record        000000000000002C (44)
                        00 00 00 00
CAA3A20 CAA3A20 0000001 2E          Created Zip Spec      2E (46) '4.6'
CAA3A21 CAA3A21 0000001 00          Created OS            00 (0) 'MS-DOS'
CAA3A22 CAA3A22 0000001 2E          Extract Zip Spec      2E (46) '4.6'
CAA3A23 CAA3A23 0000001 00          Extract OS            00 (0) 'MS-DOS'
CAA3A24 CAA3A27 0000004 00 00 00 00 Number of this disk   00000000 (0)
CAA3A28 CAA3A2B 0000004 00 00 00 00 Central Dir Disk no   00000000 (0)
CAA3A2C CAA3A33 0000008 06 57 01 00 Entries in this disk  0000000000015706 (87814)
                        00 00 00 00
CAA3A34 CAA3A3B 0000008 06 57 01 00 Total Entries         0000000000015706 (87814)
                        00 00 00 00
CAA3A3C CAA3A43 0000008 A5 49 5C 00 Size of Central Dir   00000000005C49A5 (6048165)
                        00 00 00 00
CAA3A44 CAA3A4B 0000008 6F F0 4D 0C Offset to Central dir 000000000C4DF06F (206434415)
                        00 00 00 00

CAA3A4C CAA3A4F 0000004 50 4B 06 07 ZIP64 END CENTRAL DIR 07064B50 (117853008)
                                    LOCATOR
CAA3A50 CAA3A53 0000004 00 00 00 00 Central Dir Disk no   00000000 (0)
CAA3A54 CAA3A5B 0000008 14 3A AA 0C Offset to Zip64 EOCD  000000000CAA3A14 (212482580)
                        00 00 00 00
CAA3A5C CAA3A5F 0000004 01 00 00 00 Total no of Disks     00000001 (1)

CAA3A60 CAA3A63 0000004 50 4B 05 06 END CENTRAL HEADER    06054B50 (101010256)
CAA3A64 CAA3A65 0000002 00 00       Number of this disk   0000 (0)
CAA3A66 CAA3A67 0000002 00 00       Central Dir Disk no   0000 (0)
CAA3A68 CAA3A69 0000002 FF FF       Entries in this disk  FFFF (65535)
CAA3A6A CAA3A6B 0000002 FF FF       Total Entries         FFFF (65535)
CAA3A6C CAA3A6F 0000004 A5 49 5C 00 Size of Central Dir   005C49A5 (6048165)
CAA3A70 CAA3A73 0000004 6F F0 4D 0C Offset to Central Dir 0C4DF06F (206434415)
CAA3A74 CAA3A75 0000002 00 00       Comment Length        0000 (0)
EOCD and EOCD locator (repacked)
CF53843 CF53846 0000004 50 4B 06 06 ZIP64 END CENTRAL DIR 06064B50 (101075792)
                                    RECORD
CF53847 CF5384E 0000008 2C 00 00 00 Size of record        000000000000002C (44)
                        00 00 00 00
CF5384F CF5384F 0000001 1E          Created Zip Spec      1E (30) '3.0'
CF53850 CF53850 0000001 03          Created OS            03 (3) 'Unix'
CF53851 CF53851 0000001 2D          Extract Zip Spec      2D (45) '4.5'
CF53852 CF53852 0000001 00          Extract OS            00 (0) 'MS-DOS'
CF53853 CF53856 0000004 00 00 00 00 Number of this disk   00000000 (0)
CF53857 CF5385A 0000004 00 00 00 00 Central Dir Disk no   00000000 (0)
CF5385B CF53862 0000008 06 57 01 00 Entries in this disk  0000000000015706 (87814)
                        00 00 00 00
CF53863 CF5386A 0000008 06 57 01 00 Total Entries         0000000000015706 (87814)
                        00 00 00 00
CF5386B CF53872 0000008 35 72 7C 00 Size of Central Dir   00000000007C7235 (8155701)
                        00 00 00 00
CF53873 CF5387A 0000008 0E C6 78 0C Offset to Central dir 000000000C78C60E (209241614)
                        00 00 00 00

CF5387B CF5387E 0000004 50 4B 06 07 ZIP64 END CENTRAL DIR 07064B50 (117853008)
                                    LOCATOR
CF5387F CF53882 0000004 00 00 00 00 Central Dir Disk no   00000000 (0)
CF53883 CF5388A 0000008 43 38 F5 0C Offset to Zip64 EOCD  000000000CF53843 (217397315)
                        00 00 00 00
CF5388B CF5388E 0000004 01 00 00 00 Total no of Disks     00000001 (1)

CF5388F CF53892 0000004 50 4B 05 06 END CENTRAL HEADER    06054B50 (101010256)
CF53893 CF53894 0000002 00 00       Number of this disk   0000 (0)
CF53895 CF53896 0000002 00 00       Central Dir Disk no   0000 (0)
CF53897 CF53898 0000002 FF FF       Entries in this disk  FFFF (65535)
CF53899 CF5389A 0000002 FF FF       Total Entries         FFFF (65535)
CF5389B CF5389E 0000004 35 72 7C 00 Size of Central Dir   007C7235 (8155701)
CF5389F CF538A2 0000004 0E C6 78 0C Offset to Central Dir 0C78C60E (209241614)
CF538A3 CF538A4 0000002 00 00       Comment Length        0000 (0)

Thanks for the analysis. I'll try the suggested fixes an let you know.

@chenxiaolong That was a good guess.

Changing the "Created Zip Spec" record to 0x2D (via the Python program) didn't help but changing "Extract Zip Spec" (or both) did.

Here is the "ZIP64 END CENTRAL DIR" header from the modified file that will open in Excel:

CAA3B48 0000004 50 4B 06 06 ZIP64 END CENTRAL DIR 06064B50
                            RECORD
CAA3B4C 0000008 2C 00 00 00 Size of record        000000000000002C
                00 00 00 00
CAA3B54 0000001 2E          Created Zip Spec      2E '4.6'
CAA3B55 0000001 00          Created OS            00 'MS-DOS'
CAA3B56 0000001 2D          Extract Zip Spec      2D '4.5'
CAA3B57 0000001 00          Extract OS            00 'MS-DOS'
CAA3B58 0000004 00 00 00 00 Number of this disk   00000000
CAA3B5C 0000004 00 00 00 00 Central Dir Disk no   00000000
CAA3B60 0000008 06 57 01 00 Entries in this disk  0000000000015706
                00 00 00 00
CAA3B68 0000008 06 57 01 00 Total Entries         0000000000015706
                00 00 00 00
CAA3B70 0000008 A5 49 5C 00 Size of Central Dir   00000000005C49A5
                00 00 00 00
CAA3B78 0000008 A3 F1 4D 0C Offset to Central dir 000000000C4DF1A3
                00 00 00 00

CAA3B80 0000004 50 4B 06 07 ZIP64 END CENTRAL DIR 07064B50
                            LOCATOR
CAA3B84 0000004 00 00 00 00 Central Dir Disk no   00000000
CAA3B88 0000008 48 3B AA 0C Offset to Central dir 000000000CAA3B48
                00 00 00 00
CAA3B90 0000004 01 00 00 00 Total no of Disks     00000001

CAA3B94 0000004 50 4B 05 06 END CENTRAL HEADER    06054B50
CAA3B98 0000002 00 00       Number of this disk   0000
CAA3B9A 0000002 00 00       Central Dir Disk no   0000
CAA3B9C 0000002 FF FF       Entries in this disk  FFFF
CAA3B9E 0000002 FF FF       Total Entries         FFFF
CAA3BA0 0000004 A5 49 5C 00 Size of Central Dir   005C49A5
CAA3BA4 0000004 A3 F1 4D 0C Offset to Central Dir 0C4DF1A3
CAA3BA8 0000002 00 00       Comment Length        0000
Done

Awesome, glad that worked!

I didn't notice I accidentally grabbed the Created Zip Spec offset. Good catch.

For additional context, I managed to get the basic-re zip.xlsx file (i.e., the good one) opened and resaved in Excel.

Here is the "ZIP64 END CENTRAL DIR" header from the Excel generated file. It seems like Excel prefers the 4.5 Spec:

E4A4980 0000004 50 4B 06 06 ZIP64 END CENTRAL DIR 06064B50
                            RECORD
E4A4984 0000008 2C 00 00 00 Size of record        000000000000002C
                00 00 00 00
E4A498C 0000001 2D          Created Zip Spec      2D '4.5'
E4A498D 0000001 00          Created OS            00 'MS-DOS'
E4A498E 0000001 2D          Extract Zip Spec      2D '4.5'
E4A498F 0000001 00          Extract OS            00 'MS-DOS'
E4A4990 0000004 00 00 00 00 Number of this disk   00000000
E4A4994 0000004 00 00 00 00 Central Dir Disk no   00000000
E4A4998 0000008 DE 56 01 00 Entries in this disk  00000000000156DE
                00 00 00 00
E4A49A0 0000008 DE 56 01 00 Total Entries         00000000000156DE
                00 00 00 00
E4A49A8 0000008 DD 3E 5C 00 Size of Central Dir   00000000005C3EDD
                00 00 00 00
E4A49B0 0000008 A3 0A EE 0D Offset to Central dir 000000000DEE0AA3
                00 00 00 00

E4A49B8 0000004 50 4B 06 07 ZIP64 END CENTRAL DIR 07064B50
                            LOCATOR
E4A49BC 0000004 00 00 00 00 Central Dir Disk no   00000000
E4A49C0 0000008 80 49 4A 0E Offset to Central dir 000000000E4A4980
                00 00 00 00
E4A49C8 0000004 01 00 00 00 Total no of Disks     00000001

E4A49CC 0000004 50 4B 05 06 END CENTRAL HEADER    06054B50
E4A49D0 0000002 00 00       Number of this disk   0000
E4A49D2 0000002 00 00       Central Dir Disk no   0000
E4A49D4 0000002 FF FF       Entries in this disk  FFFF
E4A49D6 0000002 FF FF       Total Entries         FFFF
E4A49D8 0000004 DD 3E 5C 00 Size of Central Dir   005C3EDD
E4A49DC 0000004 A3 0A EE 0D Offset to Central Dir 0DEE0AA3
E4A49E0 0000002 00 00       Comment Length        0000

If the DEFAULT_VERSION value in zip.rs was configurable from 4.6 to 4.5 it would fix this issue.

The > 65k file entries is a little bit unusual but there are more common cases of xlsx files that need ZIP64 support and the 4.6/4.5 issue would block those.

it looks like version 4.6 only introduced support for bzip2-compressed entries and the sample files don't contain any of those.

In that case would a fix to only use 4.6 when the bzip2 feature is enabled be suitable? Something like this:

diff --git a/src/types.rs b/src/types.rs
index c3d0a45..e2f44d4 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -272,7 +272,10 @@ impl TryFrom<OffsetDateTime> for DateTime {
     }
 }
 
+#[cfg(feature = "bzip2")]
 pub const DEFAULT_VERSION: u8 = 46;
+#[cfg(not(feature = "bzip2"))]
+pub const DEFAULT_VERSION: u8 = 45;
 
 /// A type like `AtomicU64` except it implements `Clone` and has predefined
 /// ordering.

Adding @mvdnes who updated the default version in 6029527

mvdnes commented

It looks to me that the version needed to extract in the zip64 record still uses the old fixed method. The non-64 version is already written dynamically, as can be seen here.

Should be easy enough to fix.

It looks to me that the version needed to extract in the zip64 record still uses the old fixed method. The non-64 version is already written dynamically, as can be seen here.

@mvdnes thanks for the pointer.

I initially thought of something like this:

diff --git a/src/write.rs b/src/write.rs
index 4cdc031..f9ef3fa 100644
--- a/src/write.rs
+++ b/src/write.rs
@@ -836,12 +836,19 @@ impl<W: Write + io::Seek> ZipWriter<W> {
             }
             let central_size = writer.stream_position()? - central_start;
 
+            let max_version_needed = self
+                .files
+                .iter()
+                .map(|f| f.version_needed())
+                .max()
+                .unwrap_or(DEFAULT_VERSION as u16);
+
             if self.files.len() > spec::ZIP64_ENTRY_THR
                 || central_size.max(central_start) > spec::ZIP64_BYTES_THR
             {
                 let zip64_footer = spec::Zip64CentralDirectoryEnd {
-                    version_made_by: DEFAULT_VERSION as u16,
-                    version_needed_to_extract: DEFAULT_VERSION as u16,
+                    version_made_by: max_version_needed,
+                    version_needed_to_extract: max_version_needed,
                     disk_number: 0,
                     disk_with_central_directory: 0,
                     number_of_files_on_this_disk: self.files.len() as u64,

However, that will only set the version the version to 4.5/45 if one of the file sizes is > 4GB and not for the case where the number of files > 64k. So that probably isn't correct. My previous "#[cfg()]" suggestion works but it seems a little crude.

Any suggestions on a better way to handle this?

Closing here and moving to zip2: zip-rs/zip2#100