[英]Can I create and Save a file in Azure storage at the same time?
我正在嘗試創建 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);
}
}
}
我在 memory 中創建文件,然后將其復制並上傳到 azure。
我的“問題”是為此我需要首先將整個文件保存在 memory 中,然后才開始復制(如果文件太大並且機器內存不足,這可能是一個問題)。
Ideally i could write directly into azure or as soon as i filled my memory stream buffer i would copy it to azure and then write again on top of it instead of allocating more space in me memory stream buffer.
有沒有辦法直接寫入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
並繼續向其中添加數據。 添加所有數據后,創建一個字節數組,然后從中創建一個 memory stream 並上傳 memory2F23E544CFAFDZ9196C84
這是示例代碼(雖然未經測試):
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);
}
我將有一個 go,主要基於Gaurav Mantri-AIS 的回答。 因為我認為你們正在做某事。
讓我們在這里共同努力...一方面,您希望盡快寫入 Blob 以限制 memory 的使用。 另一方面,我們不想寫每一行,因為這超過了塊限制。 所以我們需要在 memory 中有X 條記錄,然后再將其寫入 blob。
我在這里嘗試使用X值為 50 的一些偽代碼。我認為這個值可以(並且應該)針對 memory 的使用、性能和塊數進行優化:
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;
StringBuilder builder = new StringBuilder();
foreach (var row in reportEntries)
{
builder.Append($"\"{row.Date}\",\"{row.StoreId}\",\"{row.ItemId}\",\"{row.SalesQuantity}\"");
counter++;
if (counter % 50 == 0)
{
var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(counter.ToString("d6")));
blob.PutBlock(blockId, new MemoryStream(Encoding.UTF8.GetBytes(line)), string.Empty);
builder = new StringBuilder();
blockIds.Add(blockId);
}
}
// Check if there's anything still in the String Builder and write it
if (builder.Length != 0)
{
var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(counter.ToString("d6")));
blob.PutBlock(blockId, new MemoryStream(Encoding.UTF8.GetBytes(line)), string.Empty);
}
blob.PutBlockList(blockIds);
}
要考慮的另一件事是,Azure Function 存儲綁定使您能夠將 blob 綁定到Stream
。 這讓我有兩件事需要考慮:
編輯:
我深入研究了azure-webjobs-sdk
的源代碼,發現它使用CloudBlobStream
。 盡管它被標記為舊版,但您仍然可以通過在CloudBlobStream
上調用OpenWriteAsync
來獲取CloudBlockBlob
。 我沒有時間測試一個例子,但我確實在這里找到了這個例子: Uploading a file to Azure Blob on the fly 。
public async Task<Stream> GetWriteStreamAsync(string storagePath, string contentType)
{
var blockBlob = blobContainer.GetBlockBlobReference(storagePath);
blockBlob.Properties.ContentType = contentType;
CloudBlobStream bb = await blockBlob.OpenWriteAsync();
return bb;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.