Skip to content

Commit d0e4a86

Browse files
authored
ZipArchiveEntry breaking change (#41300)
1 parent a16dd40 commit d0e4a86

File tree

4 files changed

+78
-7
lines changed

4 files changed

+78
-7
lines changed

docs/core/compatibility/9.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ If you're migrating an app to .NET 9, the breaking changes listed here might aff
2525

2626
| Title | Type of change | Introduced version |
2727
|------------------------------------------------------------------------------------------|---------------------|--------------------|
28+
| [Adding a ZipArchiveEntry with CompressionLevel sets ZIP central directory header general-purpose bit flags](core-libraries/9.0/compressionlevel-bits.md) | Behavioral change | Preview 5 |
2829
| [API obsoletions with custom diagnostic IDs](core-libraries/9.0/obsolete-apis-with-custom-diagnostics.md) | Source incompatible | Preview 1 |
2930
| [Creating type of array of System.Void not allowed](core-libraries/9.0/type-instance.md) | Behavioral change | Preview 1 |
3031
| [Inline array struct size limit is enforced](core-libraries/9.0/inlinearray-size.md) | Behavioral change | Preview 1 |
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: "Breaking change: Adding a ZipArchiveEntry with CompressionLevel sets ZIP central directory header general-purpose bit flags"
3+
description: Learn about the .NET 9 breaking change in core .NET libraries where adding a ZipArchiveEntry with a specified CompressionLevel sets the ZIP central directory header general-purpose bit flags.
4+
ms.date: 06/03/2024
5+
---
6+
# Adding a ZipArchiveEntry with CompressionLevel sets ZIP central directory header general-purpose bit flags
7+
8+
The [ZIP file specification](https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE_6.2.0.txt) defines that bits 1 & 2 of the general-purpose bit flag in a nested file record's central directory header should be used to indicate the compression level of the nested file.
9+
10+
.NET Framework sets these bits when generating the ZIP files underpinning the <xref:System.IO.Packaging.ZipPackage> API. During the migration of .NET Framework code to .NET, this functionality was lost and in .NET, bits 1 & 2 were always set to `0` when new file records were created within the ZIP file. This breaking change restores that capability. However, existing .NET clients that specify a <xref:System.IO.Packaging.CompressionOption> when calling <xref:System.IO.Compression.ZipArchive.CreateEntry%2A?displayProperty=nameWithType> will see the general-purpose bit flag values change.
11+
12+
## Previous behavior
13+
14+
Previously, .NET preserved the general-purpose bits for every <xref:System.IO.Compression.ZipArchiveEntry> already in a <xref:System.IO.Compression.ZipArchive> when it was loaded and new entries were added. However, calling <xref:System.IO.Compression.ZipArchive.CreateEntry(System.String,System.IO.Compression.CompressionLevel)?displayProperty=nameWithType> always resulted in bits 1 & 2 being left at a default value of `0`, even if a <xref:System.IO.Compression.CompressionLevel> other than <xref:System.IO.Compression.CompressionLevel.Optimal?displayProperty=nameWithType> was used.
15+
16+
This behavior had a downstream effect: calling <xref:System.IO.Packaging.Package.CreatePart(System.Uri,System.String,System.IO.Packaging.CompressionOption)?displayProperty=nameWithType> with any <xref:System.IO.Packaging.CompressionOption> resulted in bits 1 & 2 being left unset (and thus the <xref:System.IO.Packaging.CompressionOption> was always persisted to the ZIP file as <xref:System.IO.Packaging.CompressionOption.Normal?displayProperty=nameWithType>).
17+
18+
## New behavior
19+
20+
Starting in .NET 9, the <xref:System.IO.Compression.CompressionLevel> parameter is mapped to the general-purpose bit flags as indicated in the following table.
21+
22+
| `CompressionLevel` | Bit 1 | Bit 2 |
23+
|--------------------|-------|-------|
24+
| `NoCompression` | 0 | 0 |
25+
| `Optimal` | 0 | 0 |
26+
| `SmallestSize` | 1 | 0 |
27+
| `Fastest` | 1 | 1 |
28+
29+
If you call <xref:System.IO.Compression.ZipArchive.CreateEntry(System.String,System.IO.Compression.CompressionLevel)?displayProperty=nameWithType> and specify <xref:System.IO.Compression.CompressionLevel.NoCompression?displayProperty=nameWithType>, the nested file record's compression method is set to `Stored` (rather than the default value of `Deflate`.)
30+
31+
The general-purpose bits for `ZipArchiveEntry` records that already exist in a `ZipArchive` are still preserved if a new `ZipArchiveEntry` is added.
32+
33+
The `CompressionOption` enumeration values in <xref:System.IO.Packaging.Package.CreatePart(System.Uri,System.String,System.IO.Packaging.CompressionOption)?displayProperty=nameWithType> are mapped to `CompressionLevel` (and result in the corresponding bits being set) as shown in the following table.
34+
35+
| `CompressionOption` | `CompressionLevel` |
36+
|---------------------|------------------------------------------------------|
37+
| `NotCompressed` | `NoCompression` |
38+
| `Normal` | `Optimal` |
39+
| `Maximum` | `SmallestSize` (.NET Framework)<br/>`Optimal` (.NET) |
40+
| `Fast` | `Fastest` |
41+
| `SuperFast` | `Fastest` |
42+
43+
## Version introduced
44+
45+
.NET 9 Preview 5
46+
47+
## Type of breaking change
48+
49+
This change is a [behavioral change](../../categories.md#behavioral-change).
50+
51+
## Reason for change
52+
53+
This breaking change was introduced to restore the existing .NET Framework behavior, which was omitted from .NET at the point of porting. This change also gives downstream clients (such as <xref:System.IO.Packaging>) control over the value of these bits.
54+
55+
## Recommended action
56+
57+
If you want to ensure that new `ZipArchiveEntry` records added to the `ZipArchive` have general-purpose bit flags of `0`, specify a `CompressionLevel` of `CompressionLevel.Optimal` or `CompressionLevel.NoCompression` when you call <xref:System.IO.Compression.ZipArchive.CreateEntry(System.String,System.IO.Compression.CompressionLevel)?displayProperty=nameWithType>.
58+
59+
If you use <xref:System.IO.Packaging.Package.CreatePart(System.Uri,System.String,System.IO.Packaging.CompressionOption)?displayProperty=nameWithType> to add parts to an OPC package, specify a `CompressionOption` of `CompressionOption.NotCompressed` or `CompressionOption.Normal`.
60+
61+
## Affected APIs
62+
63+
- <xref:System.IO.Compression.ZipArchive.CreateEntry(System.String,System.IO.Compression.CompressionLevel)?displayProperty=fullName>
64+
- <xref:System.IO.Packaging.Package.CreatePart(System.Uri,System.String,System.IO.Packaging.CompressionOption)?displayProperty=fullName>

docs/core/compatibility/toc.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ items:
1616
items:
1717
- name: API obsoletions with custom diagnostic IDs
1818
href: core-libraries/9.0/obsolete-apis-with-custom-diagnostics.md
19+
- name: Adding a ZipArchiveEntry sets header general-purpose bit flags
20+
href: core-libraries/9.0/compressionlevel-bits.md
1921
- name: Creating type of array of System.Void not allowed
2022
href: core-libraries/9.0/type-instance.md
2123
- name: Inline array struct size limit is enforced
@@ -1159,7 +1161,7 @@ items:
11591161
- name: Default ASP.NET Core port changed to 8080
11601162
href: containers/8.0/aspnet-port.md
11611163
- name: Kerberos package removed from Alpine and Debian images
1162-
href: containers/8.0/krb5-libs-package.md
1164+
href: containers/8.0/krb5-libs-package.md
11631165
- name: libintl package removed from Alpine images
11641166
href: containers/8.0/libintl-package.md
11651167
- name: Multi-platform container tags are Linux-only
@@ -1170,6 +1172,10 @@ items:
11701172
items:
11711173
- name: .NET 9
11721174
items:
1175+
- name: API obsoletions with custom diagnostic IDs
1176+
href: core-libraries/9.0/obsolete-apis-with-custom-diagnostics.md
1177+
- name: Adding a ZipArchiveEntry sets header general-purpose bit flags
1178+
href: core-libraries/9.0/compressionlevel-bits.md
11731179
- name: Creating type of array of System.Void not allowed
11741180
href: core-libraries/9.0/type-instance.md
11751181
- name: Inline array struct size limit is enforced

docs/fundamentals/code-analysis/code-quality-rule-options.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ Each refining option can be configured for all rules, for a category of rules (f
2525
2626
The syntax for configuring an option for *all* rules is as follows:
2727
28-
|Syntax|Example|
29-
|-|-|
28+
| Syntax | Example |
29+
|----------------------------------------------------|--------------------------------------------|
3030
| dotnet_code_quality.\<OptionName> = \<OptionValue> | `dotnet_code_quality.api_surface = public` |
3131
3232
The values for `<OptionName>` are listed under [Options](#options).
@@ -35,8 +35,8 @@ The values for `<OptionName>` are listed under [Options](#options).
3535
3636
The syntax for configuring an option for a [*category* of rules](categories.md) is as follows:
3737
38-
|Syntax|Example|
39-
|-|-|
38+
| Syntax | Example |
39+
|-----------------------------------------------------------------|---------------------------------------------------|
4040
| dotnet_code_quality.\<RuleCategory>.\<OptionName> = OptionValue | `dotnet_code_quality.Naming.api_surface = public` |
4141
4242
The following table lists the available values for `<RuleCategory>`.
@@ -65,8 +65,8 @@ The following table lists the available values for `<RuleCategory>`.
6565
6666
The syntax for configuring an option for a *specific* rule is as follows:
6767
68-
|Syntax|Example|
69-
|-|-|
68+
| Syntax | Example |
69+
|--------------------------------------------------------------|---------------------------------------------------|
7070
| dotnet_code_quality.\<RuleId>.\<OptionName> = \<OptionValue> | `dotnet_code_quality.CA1040.api_surface = public` |
7171
7272
## Options

0 commit comments

Comments
 (0)