繁体   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