c# - 我可以同时在 Azure 存储中创建和保存文件吗?
问题描述
我正在尝试创建 CSV 文件并将其导入 Azure 存储帐户。
public static void ExportCsvToStorageAccount(string fileName, CloudBlobContainer container, IEnumerable<ReportRow> reportEntries)
{
using (var ms = new MemoryStream())
{
using (var file = new StreamWriter(ms))
{
file.WriteLine("Date,StoreId,ItemId,SalesQuantity");
foreach (var row in reportEntries)
{
var line = $"\"{row.Date}\",\"{row.StoreId}\",\"{row.ItemId}\",\"{row.SalesQuantity}\"";
file.WriteLine(line);
}
var blockBlob = container.GetBlockBlobReference($"{fileName}.csv");
ms.Position = 0;
blockBlob.UploadFromStream(ms);
}
}
}
我在内存中创建文件,然后将其复制并上传到天蓝色。
我的“问题”是为此我需要首先将整个文件保存在内存中,然后才开始复制(如果文件太大并且机器内存不足,这可能是一个问题)。
理想情况下,我可以直接写入 azure,或者一旦我填满内存流缓冲区,我就会将其复制到 azure,然后在其顶部再次写入,而不是在我的内存流缓冲区中分配更多空间。
有没有办法直接写入 Azure?(目标是节省内存)
编辑:
通过 Gaurav Mantri-AIS 输入的答案,我想出了这个(因为我有超过 50000 个条目,这是块的限制),
public static void ExportCSVToStorageAccount(string fileName, CloudBlobContainer container, IEnumerable<RawReportRow> reportEntries)
{
var blob = container.GetAppendBlobReference($"{fileName}.csv");
blob.CreateOrReplace();
blob.AppendText($"Date,StoreId,ItemId,SalesQuantity{Environment.NewLine}");
foreach (var row in reportEntries)
{
var line = $"\"{row.Date}\",\"{row.StoreId}\",\"{row.ItemId}\",\"{row.SalesQuantity}\"{Environment.NewLine}";
blob.AppendText(line);
}
}
这个解决方案的问题是它需要的时间太长,从 5 分钟到一个多小时。我可能做错了什么,因为 AppendBlob 应该执行良好的附加,但似乎并非如此。
关于如何提高写入速度的任何想法?
解决方案
这样做当然是可能的。一种解决方案是使用StringBuilder
并继续向其中添加数据。添加完所有数据后,创建一个字节数组,然后从中创建一个内存流并上传该内存流。
这是示例代码(虽然未经测试):
public static void ExportCsvToStorageAccount(string fileName, CloudBlobContainer container, IEnumerable<ReportRow> reportEntries)
{
using (var ms = new MemoryStream())
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Date,StoreId,ItemId,SalesQuantity");
foreach (var row in reportEntries)
{
var line = $"\"{row.Date}\",\"{row.StoreId}\",\"{row.ItemId}\",\"{row.SalesQuantity}\"";
sb.AppendLine(line);
}
var buffer = Encoding.UTF8.GetBytes(sb.ToString());
ms.Write(buffer, 0, buffer.Length);
var blockBlob = container.GetBlockBlobReference($"{fileName}.csv");
ms.Position = 0;
blockBlob.UploadFromStream(ms);
}
}
更新
假设您使用的是 SDK 版本 9.3.3,您可以使用UploadText
方法并将字符串直接上传到 Azure 存储。就像是:
public static void ExportCsvToStorageAccount(string fileName, CloudBlobContainer container, IEnumerable<string> reportEntries)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Date,StoreId,ItemId,SalesQuantity");
foreach (var row in reportEntries)
{
var line = $"\"{row.Date}\",\"{row.StoreId}\",\"{row.ItemId}\",\"{row.SalesQuantity}\"";
sb.AppendLine(line);
}
var blockBlob = container.GetBlockBlobReference($"{fileName}.csv");
blockBlob.UploadText(sb.ToString());
}
更新 2
另一种选择是将每一行作为单独的块上传,然后最终提交块列表。但是请记住,一个 blob 中只能有 50000 个块,如果您的数据中有超过 50000 条记录,此方法将失败。为了规避这个限制,您可能希望合并某些记录并将它们保存为一个块。
这是示例代码:
public static void ExportCsvToStorageAccount(string fileName, CloudBlobContainer container, IEnumerable<string> reportEntries)
{
List<string> blockIds = new List<string>();
CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
int counter = 0;
foreach (var row in reportEntries)
{
var line = $"\"{row.Date}\",\"{row.StoreId}\",\"{row.ItemId}\",\"{row.SalesQuantity}\"";
var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(counter.ToString("d6")));
blob.PutBlock(blockId, new MemoryStream(Encoding.UTF8.GetBytes(line)), string.Empty);
blockIds.Add(blockId);
counter++;
}
blob.PutBlockList(blockIds);
}
推荐阅读
- html - 如何使用thymeleaf和系统本地文件夹中的spring boot设置图像路径
- ajax - (Symfony4 中的 Javascript) - 路径功能不起作用
- python - 为什么在Python中使用Button函数时文本参数响应循环而命令参数不响应
- python - 无法将熊猫数据框保存到 csv
- string - 使用 pandas 数据框对列表进行迭代时字符串查找失败
- android - Volley 发送空图像
- javascript - 多个孩子的 Vue.js 道具
- connection-pooling - 空标头的 Vert.x Java Webclient 超时保持连接?
- angular - 在 ng-sidebar 中显示 nb-datepicker
- deep-learning - 在 Keras 2.0 中使用合并