簡體   English   中英

我可以同時在 Azure 存儲中創建和保存文件嗎?

[英]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 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM