如何在 .NET Core 2.1 中生成包含用户个人数据的 ZIP 文件以符合 GDPR






4.78/5 (8投票s)
如何在 .NET Core 2.1 中生成包含用户个人数据的 ZIP 文件以符合 GDPR
引言
我有一个基于 .NET Core 2.1 构建的网站,我希望它符合 GDPR,因此我需要提供一种下载用户数据的方式。在这个网站中,每个用户可以在根目录下的子目录中拥有文件,此外数据库中还保存了信息。我希望能够提供一个包含他所有文件和包含他所有个人数据的字典的 ZIP 文件。
背景
这个 链接 提供了一个使用最少线程和内存使用量动态生成 ZIP 文件的示例。
.NET Core 2.1 提供了生成的代码模板以符合 GDPR,您可以阅读更多信息 此处。
Using the Code
首先,我创建了所需的 fileCallbackResult
和 WriteOnlyStreamWrapper
类,遵循背景链接中的第一个链接。
在 .NET Core 生成的 DownloadPersonalData
Razor 页面中,ApplicationUser
中的个人信息被添加到字典中并下载为 JSON。我希望能够下载一个包含任何信息和属于用户的现有文件的 ZIP 文件。
为了做到这一点,我添加了以下方法
private async Task<Dictionary<string, string>> ReadClient(ApplicationUser user, string UserId)
{
var personalData = new Dictionary<string, string>();
//get the data from the identity application user
var personalDataProps = typeof(ApplicationUser).GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute)));
foreach (var p in personalDataProps)
{
personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null");
}
//get any other data the client may have in the database
User client = await _context.User.AsNoTracking().FirstOrDefaultAsync(b => b.UserID == UserId);
personalData.Add("ClientName", client.Name);
personalData.Add("ClientEmail", client.Email);
......
return personalData;
}
此方法构建包含数据库中所有数据的字典。
private Dictionary<string, string> ListClientFiles(string UserId)
{
//Calculate the client directory in the file system
var clientpath = GetClientPath(UserId);
var filenamesAndUrls = new Dictionary<string, string>();
if (Directory.Exists(Path.GetDirectoryName(clientpath)))
{
DirectoryInfo d = new DirectoryInfo(clientpath);
//Get recursively all the files in the directory
FileInfo[] Files = d.GetFiles("*.*", SearchOption.AllDirectories);
foreach (FileInfo file in Files)
{
string fileName = Path.GetFileNameWithoutExtension(file.Name);
string fileExtension = file.Extension;
//in the case of having the same fileName in different directories
//inside the client directory, suffix the number i.e. profile.html and profile(1).html
if (filenamesAndUrls.ContainsKey(fileName + fileExtension)) {
int copy = 0;
do {
copy++;
} while (filenamesAndUrls.ContainsKey(fileNname + "(" + copy + ")" + fileExtension));
fileName = fileName +"("+copy+")";
}
fileName = fileName + fileExtension;
filenamesAndUrls.Add(fileName, file.FullName);
}
}
return filenamesAndUrls;
}
此方法创建包含要压缩的文件键和完整路径的字典。
最后,我修改了 DownloadPersonalData
Razor 页面中的 OnPostAsync
方法以执行以下操作
Dictionary<string, string> personalData = await ReadClient(user, UserId);
var filenamesAndUrls = ListClientFiles(UserId);
return new FileCallbackResult(new MediaTypeHeaderValue("application/octet-stream"),
async (outputStream, _) =>
{
using (var zipArchive = new ZipArchive(new WriteOnlyStreamWrapper(outputStream),
ZipArchiveMode.Create))
{
//the personal data dictionary is saved in a Json file
var userZipEntry = zipArchive.CreateEntry("personaldata.json");
using (var userZipStream = userZipEntry.Open())
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes
(JsonConvert.SerializeObject(personalData))) )
await stream.CopyToAsync(userZipStream);
//all other personal files
foreach (var kvp in filenamesAndUrls)
{
var zipEntry = zipArchive.CreateEntry(kvp.Key);
using (var zipStream = zipEntry.Open())
using (var stream = System.IO.File.OpenRead(kvp.Value))
await stream.CopyToAsync(zipStream);
}
}
})
{
FileDownloadName = "DownloadPersonalData.zip"
};
因此,包含个人数据 - 或来自数据库的任何其他相关数据 - 的字典作为 ZIP 条目通过内存流插入到 ZIP 文件中,无需将其保存到临时文件中。如果您更愿意,您可以为数据库中不同表中的不同信息创建多个 ZIP 条目。
其他个人文件被读取并作为其他 ZIP 条目插入到 ZIP 文件中。
历史
- 2018 年 9 月 24 日:初始版本