Skip to content

Commit 1f1ac65

Browse files
authored
feat(IZipArchiveService): add ArchiveAsync method (#7373)
* feat(ZipArchiveService): add ArchiveDirectoryAsync method * test: 增加单元测试 * doc: 更新文档注释 * refactor: 精简代码 * refactor: 增加文件检查 * refactor: 更新检查条件 * doc: 归档项独立成一个文件
1 parent c045592 commit 1f1ac65

4 files changed

Lines changed: 136 additions & 154 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
5+
6+
using System.IO.Compression;
7+
8+
/// <summary>
9+
/// 归档项实体类
10+
/// </summary>
11+
public readonly record struct ArchiveEntry
12+
{
13+
/// <summary>
14+
/// 获得 物理文件
15+
/// </summary>
16+
public string SourceFileName { get; init; }
17+
18+
/// <summary>
19+
/// 获得 归档项
20+
/// </summary>
21+
public string EntryName { get; init; }
22+
23+
/// <summary>
24+
/// 获得 压缩配置
25+
/// </summary>
26+
public CompressionLevel? CompressionLevel { get; init; }
27+
}

src/BootstrapBlazor/Services/DefaultZipArchiveService.cs

Lines changed: 24 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -13,63 +13,61 @@ class DefaultZipArchiveService : IZipArchiveService
1313
/// <summary>
1414
/// <inheritdoc/>
1515
/// </summary>
16-
/// <param name="files">要归档的文件集合</param>
17-
/// <param name="options">归档配置</param>
18-
/// <returns>归档数据流</returns>
19-
public async Task<Stream> ArchiveAsync(IEnumerable<string> files, ArchiveOptions? options = null)
16+
public async Task<Stream> ArchiveAsync(IEnumerable<ArchiveEntry> entries, ArchiveOptions? options = null)
2017
{
2118
var stream = new MemoryStream();
2219
options ??= new ArchiveOptions();
2320
options.LeaveOpen = true;
24-
await ArchiveFilesAsync(stream, files, options);
21+
await ArchiveFilesAsync(stream, entries, options);
2522
stream.Position = 0;
2623
return stream;
2724
}
2825

2926
/// <summary>
3027
/// <inheritdoc/>
3128
/// </summary>
32-
/// <param name="archiveFile">归档文件</param>
33-
/// <param name="files">要归档的文件集合</param>
34-
/// <param name="options">归档配置</param>
35-
public async Task ArchiveAsync(string archiveFile, IEnumerable<string> files, ArchiveOptions? options = null)
29+
public async Task ArchiveAsync(string archiveFile, IEnumerable<ArchiveEntry> entries, ArchiveOptions? options = null)
3630
{
3731
using var stream = File.OpenWrite(archiveFile);
38-
await ArchiveFilesAsync(stream, files, options);
32+
await ArchiveFilesAsync(stream, entries, options);
3933
}
4034

41-
private static async Task ArchiveFilesAsync(Stream stream, IEnumerable<string> files, ArchiveOptions? options = null)
35+
private static async Task ArchiveFilesAsync(Stream stream, IEnumerable<ArchiveEntry> entries, ArchiveOptions? options = null)
4236
{
4337
options ??= new ArchiveOptions();
4438
using var archive = new ZipArchive(stream, options.Mode, options.LeaveOpen, options.Encoding);
45-
foreach (var f in files)
39+
foreach (var f in entries)
4640
{
4741
if (options.ReadStreamAsync != null)
4842
{
49-
var entry = archive.CreateEntry(Path.GetFileName(f), options.CompressionLevel);
50-
using var entryStream = entry.Open();
51-
await using var content = await options.ReadStreamAsync(f);
43+
var entry = archive.CreateEntry(f.EntryName, options.CompressionLevel);
44+
await using var content = await options.ReadStreamAsync(f.SourceFileName);
45+
await using var entryStream = entry.Open();
5246
await content.CopyToAsync(entryStream);
5347
}
54-
else
48+
else if (Directory.Exists(f.SourceFileName))
5549
{
56-
archive.CreateEntryFromFile(f, Path.GetFileName(f), options.CompressionLevel);
50+
var entryName = f.EntryName;
51+
if (!string.IsNullOrEmpty(entryName))
52+
{
53+
if (!entryName.EndsWith('/'))
54+
{
55+
entryName = $"{entryName}/";
56+
}
57+
archive.CreateEntry(entryName, f.CompressionLevel ?? options.CompressionLevel);
58+
}
59+
}
60+
else if (File.Exists(f.SourceFileName))
61+
{
62+
archive.CreateEntryFromFile(f.SourceFileName, f.EntryName, f.CompressionLevel ?? options.CompressionLevel);
5763
}
5864
}
5965
}
6066

6167
/// <summary>
6268
/// <inheritdoc/>
6369
/// </summary>
64-
/// <param name="archiveFile"></param>
65-
/// <param name="directoryName"></param>
66-
/// <param name="compressionLevel"></param>
67-
/// <param name="includeBaseDirectory"></param>
68-
/// <param name="encoding"></param>
69-
/// <param name="token"></param>
70-
/// <returns></returns>
71-
/// <exception cref="NotImplementedException"></exception>
72-
public async Task ArchiveDirectory(string archiveFile, string directoryName, CompressionLevel compressionLevel = CompressionLevel.Optimal, bool includeBaseDirectory = false, Encoding? encoding = null, CancellationToken token = default)
70+
public async Task ArchiveDirectoryAsync(string archiveFile, string directoryName, CompressionLevel compressionLevel = CompressionLevel.Optimal, bool includeBaseDirectory = false, Encoding? encoding = null, CancellationToken token = default)
7371
{
7472
if (Directory.Exists(directoryName))
7573
{
@@ -93,77 +91,6 @@ await Task.Run(() =>
9391
/// <summary>
9492
/// <inheritdoc/>
9593
/// </summary>
96-
/// <param name="archiveFile">归档文件</param>
97-
/// <param name="entries"></param>
98-
/// <param name="compressionLevel"></param>
99-
/// <param name="encoding"></param>
100-
/// <param name="skipEmptyFolder"></param>
101-
/// <param name="token"></param>
102-
/// <returns></returns>
103-
/// <exception cref="NotImplementedException"></exception>
104-
public async Task ArchiveDirectory(string archiveFile, IEnumerable<string> entries, CompressionLevel compressionLevel = CompressionLevel.Optimal, Encoding? encoding = null, bool skipEmptyFolder = false, CancellationToken token = default)
105-
{
106-
using var archive = ZipFile.Open(archiveFile, ZipArchiveMode.Create, encoding);
107-
108-
foreach (var entry in entries)
109-
{
110-
if (Directory.Exists(entry))
111-
{
112-
AddFolderToZip(archive, entry, Path.GetFileName(entry), compressionLevel);
113-
}
114-
else if (File.Exists(entry))
115-
{
116-
archive.CreateEntryFromFile(entry, Path.GetFileName(entry), compressionLevel);
117-
}
118-
}
119-
}
120-
121-
private static void AddFolderToZip(ZipArchive archive, string folderPath, string relativePath, CompressionLevel compressionLevel = CompressionLevel.Optimal)
122-
{
123-
archive.CreateEntry($"{relativePath}/", compressionLevel);
124-
125-
// 添加当前文件夹中的所有文件
126-
foreach (string filePath in Directory.GetFiles(folderPath))
127-
{
128-
string entryName = Path.Combine(relativePath, Path.GetFileName(filePath));
129-
archive.CreateEntryFromFile(filePath, entryName, compressionLevel);
130-
}
131-
132-
// 递归添加所有子文件夹
133-
foreach (string subfolderPath in Directory.GetDirectories(folderPath))
134-
{
135-
string newRelativePath = Path.Combine(relativePath, Path.GetFileName(subfolderPath));
136-
AddFolderToZip(archive, subfolderPath, newRelativePath, compressionLevel);
137-
}
138-
}
139-
140-
/// <summary>
141-
/// <inheritdoc/>
142-
/// </summary>
143-
/// <param name="archiveFile">归档文件</param>
144-
/// <param name="destinationDirectoryName">解压缩文件夹</param>
145-
/// <param name="overwriteFiles">是否覆盖文件 默认 false 不覆盖</param>
146-
/// <param name="encoding">编码方式 默认 null 内部使用 UTF-8</param>
147-
/// <returns></returns>
148-
public bool ExtractToDirectory(string archiveFile, string destinationDirectoryName, bool overwriteFiles = false, Encoding? encoding = null)
149-
{
150-
if (!Directory.Exists(destinationDirectoryName))
151-
{
152-
Directory.CreateDirectory(destinationDirectoryName);
153-
}
154-
ZipFile.ExtractToDirectory(archiveFile, destinationDirectoryName, encoding, overwriteFiles);
155-
return true;
156-
}
157-
158-
/// <summary>
159-
/// <inheritdoc/>
160-
/// </summary>
161-
/// <param name="archiveFile"></param>
162-
/// <param name="destinationDirectoryName"></param>
163-
/// <param name="overwriteFiles"></param>
164-
/// <param name="encoding"></param>
165-
/// <param name="token"></param>
166-
/// <returns></returns>
16794
public async Task<bool> ExtractToDirectoryAsync(string archiveFile, string destinationDirectoryName, bool overwriteFiles = false, Encoding? encoding = null, CancellationToken token = default)
16895
{
16996
if (!Directory.Exists(destinationDirectoryName))
@@ -186,11 +113,6 @@ await Task.Run(() =>
186113
/// <summary>
187114
/// <inheritdoc/>
188115
/// </summary>
189-
/// <param name="archiveFile">归档文件</param>
190-
/// <param name="entryFile">解压缩文件</param>
191-
/// <param name="overwriteFiles">是否覆盖文件 默认 false 不覆盖</param>
192-
/// <param name="encoding">编码方式 默认 null 内部使用 UTF-8</param>
193-
/// <returns></returns>
194116
public ZipArchiveEntry? GetEntry(string archiveFile, string entryFile, bool overwriteFiles = false, Encoding? encoding = null)
195117
{
196118
using var archive = ZipFile.Open(archiveFile, ZipArchiveMode.Read, encoding);

src/BootstrapBlazor/Services/IZipArchiveService.cs

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ public interface IZipArchiveService
1616
/// <summary>
1717
/// 将文件归档方法
1818
/// </summary>
19-
/// <param name="files">要归档的文件集合</param>
19+
/// <param name="entries">要归档项集合</param>
2020
/// <param name="options">归档配置</param>
2121
/// <returns>归档数据流</returns>
22-
Task<Stream> ArchiveAsync(IEnumerable<string> files, ArchiveOptions? options = null);
22+
Task<Stream> ArchiveAsync(IEnumerable<ArchiveEntry> entries, ArchiveOptions? options = null);
2323

2424
/// <summary>
2525
/// 将文件归档方法
2626
/// </summary>
2727
/// <param name="archiveFile">归档文件</param>
28-
/// <param name="files">要归档的文件集合</param>
28+
/// <param name="entries">要归档项集合</param>
2929
/// <param name="options">归档配置</param>
30-
Task ArchiveAsync(string archiveFile, IEnumerable<string> files, ArchiveOptions? options = null);
30+
Task ArchiveAsync(string archiveFile, IEnumerable<ArchiveEntry> entries, ArchiveOptions? options = null);
3131

3232
/// <summary>
3333
/// 将指定目录归档方法
@@ -38,30 +38,7 @@ public interface IZipArchiveService
3838
/// <param name="includeBaseDirectory">是否包含本目录 默认 false</param>
3939
/// <param name="encoding">编码方式 默认 null 内部使用 UTF-8</param>
4040
/// <param name="token"></param>
41-
/// <returns></returns>
42-
Task ArchiveDirectory(string archiveFile, string directoryName, CompressionLevel compressionLevel = CompressionLevel.Optimal, bool includeBaseDirectory = false, Encoding? encoding = null, CancellationToken token = default);
43-
44-
/// <summary>
45-
/// 将指定目录归档方法
46-
/// </summary>
47-
/// <param name="archiveFile">归档文件</param>
48-
/// <param name="entries">要归档条目</param>
49-
/// <param name="compressionLevel">压缩率</param>
50-
/// <param name="encoding">编码方式 默认 null 内部使用 UTF-8</param>
51-
/// <param name="skipEmptyFolder">是否跳过空文件夹</param>
52-
/// <param name="token"></param>
53-
/// <returns></returns>
54-
Task ArchiveDirectory(string archiveFile, IEnumerable<string> entries, CompressionLevel compressionLevel = CompressionLevel.Optimal, Encoding? encoding = null, bool skipEmptyFolder = false, CancellationToken token = default);
55-
56-
/// <summary>
57-
/// 解压缩归档文件到指定文件夹
58-
/// </summary>
59-
/// <param name="archiveFile">归档文件</param>
60-
/// <param name="destinationDirectoryName">解压缩文件夹</param>
61-
/// <param name="overwriteFiles">是否覆盖文件 默认 false 不覆盖</param>
62-
/// <param name="encoding">编码方式 默认 null 内部使用 UTF-8</param>
63-
/// <returns></returns>
64-
bool ExtractToDirectory(string archiveFile, string destinationDirectoryName, bool overwriteFiles = false, Encoding? encoding = null);
41+
Task ArchiveDirectoryAsync(string archiveFile, string directoryName, CompressionLevel compressionLevel = CompressionLevel.Optimal, bool includeBaseDirectory = false, Encoding? encoding = null, CancellationToken token = default);
6542

6643
/// <summary>
6744
/// 解压缩归档文件到指定文件夹异步方法
@@ -71,7 +48,6 @@ public interface IZipArchiveService
7148
/// <param name="overwriteFiles">是否覆盖文件 默认 false 不覆盖</param>
7249
/// <param name="encoding">编码方式 默认 null 内部使用 UTF-8</param>
7350
/// <param name="token"></param>
74-
/// <returns></returns>
7551
Task<bool> ExtractToDirectoryAsync(string archiveFile, string destinationDirectoryName, bool overwriteFiles = false, Encoding? encoding = null, CancellationToken token = default);
7652

7753
/// <summary>
@@ -81,6 +57,5 @@ public interface IZipArchiveService
8157
/// <param name="entryFile">解压缩文件</param>
8258
/// <param name="overwriteFiles">是否覆盖文件 默认 false 不覆盖</param>
8359
/// <param name="encoding">编码方式 默认 null 内部使用 UTF-8</param>
84-
/// <returns></returns>
8560
ZipArchiveEntry? GetEntry(string archiveFile, string entryFile, bool overwriteFiles = false, Encoding? encoding = null);
8661
}

0 commit comments

Comments
 (0)