解压缩文件并将它们保存到 Blob 存储流

Unzip files and save them to a Blob Storage stream

这个概念验证的工作流程是:

  1. Azure Function App 在 blob 存储中检测到 .zip 文件 (Stream inputBlob)
  2. 函数应用程序调用我的代码,它需要提取文件并将它们单独保存到 Blob 存储容器Stream outputBlob

代码确实从 inputBlob 获取了 .zip,我可以在调试器中看到 ZipArchive 包含 .zip 的内容。但是没有文件输出,没有错误。我需要做什么才能将所有文件保存到 outputBlob 流?我确定我遗漏了与流复制相关的内容。

[FunctionName("Name")]
public static void Run(
    [BlobTrigger("input/{name}", Connection = "AzureWebJobsStorage")]Stream inputBlob,
    [Blob("output/{name}", FileAccess.Write)] Stream outputBlob,
    string name, ILogger log)
{
    try
    {
        using var zip = new ZipArchive(inputBlob);
        
        foreach (var item in zip.Entries)
        {
            using var stream = item.Open();
            stream.CopyTo(outputBlob);
            stream.Close();
        }

        outputBlob.Seek(0, SeekOrigin.Begin);
        outputBlob.Close();
    }
    catch (Exception ex)
    {
        log.Log(LogLevel.Error, $"Error at {name}: {ex.Message}");
        throw;
    }
}

我们可以从 VSCode 进行调试以了解问题出在哪里,为此我们需要在 local.settings.json 文件中将 AzureWebJobsStorage 添加到 UseDevelopmentStorage=true

下面是 local.settings.json 文件的样子:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet",
        "unziptools_STORAGE": "DefaultEndpointsProtocol=https;AccountName=unziptools;AccountKey=XXXXXXXXX;EndpointSuffix=core.windows.net",
    }
}

与您定义的方式类似,我们需要指定 blobtrigger :

[BlobTrigger("input-files/{name}", Connection = "cloud5mins_storage")]Stream myBlob

同时获取目标容器以加载解压文件:

    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(destinationStorage);
    CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
    CloudBlobContainer container = blobClient.GetContainerReference(destinationContainer);

下面是获取输入 blob 并将其放入目标存储和容器的示例代码。

public static async Task Run([BlobTrigger("input-files/{name}", Connection = "cloud5mins_storage")]CloudBlockBlob myBlob, string name, ILogger log)
{
    log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name}");

    string destinationStorage = Environment.GetEnvironmentVariable("destinationStorage");
    string destinationContainer = Environment.GetEnvironmentVariable("destinationContainer");

    try{
        if(name.Split('.').Last().ToLower() == "zip"){

            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(destinationStorage);
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference(destinationContainer);
            
            using(MemoryStream blobMemStream = new MemoryStream()){

                await myBlob.DownloadToStreamAsync(blobMemStream);

                using(ZipArchive archive = new ZipArchive(blobMemStream))
                {
                    foreach (ZipArchiveEntry entry in archive.Entries)
                    {
                        log.LogInformation($"Now processing {entry.FullName}");

                        //Replace all NO digits, letters, or "-" by a "-" Azure storage is specific on valid characters
                        string valideName = Regex.Replace(entry.Name,@"[^a-zA-Z0-9\-]","-").ToLower();

                        CloudBlockBlob blockBlob = container.GetBlockBlobReference(valideName);
                        using (var fileStream = entry.Open())
                        {
                            await blockBlob.UploadFromStreamAsync(fileStream);
                        }
                    }
                }
            }
        }
    }
    catch(Exception ex){
        log.LogInformation($"Error! Something went wrong: {ex.Message}");

    }            
}

感谢 frankynotes

,我们有一个博客,其中包含有关此内容的详细信息

接受的答案大部分是我需要的,但有一些我不需要的非功能代码和检查。对于文档,我将工作修改后的代码放在这里:

[FunctionName("name")]
public static async Task Run(
    [BlobTrigger("input-blob-container/{name}", Connection = "AzureWebJobsStorage")]Stream inputBlob,
    string name, ILogger log)
{
    try
    {
        log.Log(LogLevel.Information, "Starting unzip.");

        string destinationStorage = Environment.GetEnvironmentVariable("destinationStorage");
        string destinationContainer = Environment.GetEnvironmentVariable("destinationContainer");

        CloudStorageAccount storageAccount  = CloudStorageAccount.Parse(destinationStorage);
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
        CloudBlobContainer container = blobClient.GetContainerReference(destinationContainer);

        using ZipArchive archive = new ZipArchive(inputBlob);
        foreach (ZipArchiveEntry entry in archive.Entries)
        {
            log.LogInformation($"Now processing {entry.FullName}");
            
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(entry.Name);
            await using var fileStream = entry.Open();
            await blockBlob.UploadFromStreamAsync(fileStream);
        }

        log.Log(LogLevel.Information, "Finished unzip.");
    }
    catch (Exception ex)
    {
        log.Log(LogLevel.Error, $"Error at {name}: {ex.Message}");
        throw;
    }
}

还有我的local.settings.json

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "destinationStorage": "DefaultEndpointsProtocol=https;AccountName=azuresitename;AccountKey=;BlobEndpoint=https://azuresitename.blob.core.windows.net/;TableEndpoint=https://azuresitename.table.core.windows.net/;QueueEndpoint=https://azuresitename.queue.core.windows.net/;FileEndpoint=https://azuresitename.file.core.windows.net/",
    "destinationContainer" : "output-blob-container" 
  }
}

我使用 Azure 扩展通过 VS Code 添加了 destinationStoragedestinationContainer 设置(在已接受答案的博客 post 中的视频中有详细介绍)。