可以同时在 Azure 存储中创建和保存文件吗?

问题描述 投票:0回答:1

我正在尝试创建一个 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,然后再写到它上面,而不是在我的内存流缓冲区中分配更多的空间。

有没有一种方法可以直接写到Azure中去?

编辑:高拉夫-曼特丽-AIS的回答,我想出了这个办法(因为我有50000多个条目,这是块的极限)。

根据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应该能很好地进行追加,但似乎不是这样。

有什么办法可以提高写入速度吗?

c# stream azure-storage-blobs
1个回答
0
投票

.当然可以这样做。一个解决办法是使用 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);
        }
    }

UPDATE

假设你使用的是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());
    }

UPDATE 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);
    }

0
投票

我打算试一试,主要是基于: Gaurav Mantri-AIS的答复. 因为我觉得你们是在做什么。

让我们一起努力... 一方面,你们希望尽快写到Blob,以限制内存使用。另一方面,我们又不想写到 每行 因为这超过了块的限制。所以我们需要有一个 X数量的记录 的内存中,然后再写入到blob中。

我在这里尝试了一些伪代码与 X 值为50。我认为这个值可以(也应该)针对内存使用、性能和块数进行优化。

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函数
  • 应该可以得到Blob的流引用。

EDIT: 我查了一下资料来源 azure-webjobs-sdk 并发现它使用了 CloudBlobStream. 尽管它被标记为遗产,但你仍然可以得到一个。CloudBlobStream 呼叫 OpenWriteAsync 关于 CloudBlockBlob. 我没有时间去测试一个例子,但我。做了 在SO上找到这个例子。在飞行中向Azure Blob上传文件.

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.