【问题标题】:Azure Function: Unzip file works in debug but not in productionAzure 功能:解压缩文件在调试中有效,但在生产中无效
【发布时间】:2020-02-06 22:09:16
【问题描述】:

这是我的第一个 Azure 函数。在将文件上传到 Azure 存储 Blob 后,我需要对其进行解压缩。
我找到了这个视频https://www.youtube.com/watch?v=GRztpy337kU 和这个帖子:https://msdevzone.wordpress.com/2017/07/07/extract-a-zip-file-stored-in-azure-blob
使用带有 C# 的 Visual Studio 2017,当我在 Visual Studio 中运行该函数时一切正常,但当我将它部署到 Azure 时,没有提取任何内容。如果我看日志,一切似乎都很好。
这是我的代码:

using System;
using System.IO;
using System.IO.Compression;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.WindowsAzure.Storage;

namespace ExtractZipFunction
{
    public static class ScompattaZip
    {
        [FunctionName("ScompattaZip")]
        public static void Run([BlobTrigger("input-files/{name}", Connection = "connectionStorage")]
                                Stream myBlob, string name, TraceWriter log)

        {
            log.Info($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
            try
            {
                string destinationStorage = Environment.GetEnvironmentVariable("destinationStorage");
                string destinationContainer = Environment.GetEnvironmentVariable("destinationContainer");
                log.Info($"destinationStorage: {destinationStorage}");
                log.Info($"destinationContainer: {destinationContainer}");

                if (System.IO.Path.GetExtension(name).ToLower() == ".zip")
                {
                    log.Info("It's a zip");
                    var archive = new ZipArchive(myBlob);
                    var storageAccount = CloudStorageAccount.Parse(destinationStorage);
                    var blobClient = storageAccount.CreateCloudBlobClient();
                    var container = blobClient.GetContainerReference(destinationContainer);

                    foreach (var entry in archive.Entries)
                    {
                        var blockBlob = container.GetBlockBlobReference(entry.FullName);
                        using (var fileStream = entry.Open())
                        {
                            if (entry.Length > 0)
                            {
                                log.Info($"Estrazione 1 - {entry.FullName}");
                                blockBlob.UploadFromStreamAsync(fileStream);
                                log.Info($"Estrazione 2 - {entry.FullName}");
                            }
                        }
                    }
                }
                else
                    log.Info("Not a zip");
            }
            catch (Exception ex)
            {
                log.Info($"Errore: {ex.Message}");
            }
        }
    }
}

这是 Azure 中的日志:

C# Blob trigger function Processed blob
 Name:EmptyJSONFile_1033.zip 
 Size: 24294 Bytes
destinationStorage: DefaultEndpointsProtocol=https;AccountName=[...]
destinationContainer: outputfiles
E' uno zip
Estrazione EmptyJSONFile_1033.ico
Estrazione 1 - EmptyJSONFile_1033.ico
Estrazione 2 - EmptyJSONFile_1033.ico
Estrazione EmptyJSONFile_1033.vstemplate
Estrazione 1 - EmptyJSONFile_1033.vstemplate
Estrazione 2 - EmptyJSONFile_1033.vstemplate
Estrazione json.json
Estrazione 1 - json.json
Estrazione 2 - json.json

看起来一切正常,但在函数结束时容器输出文件为空!

我做错了什么?

【问题讨论】:

  • 首先:请帮自己一个大忙,使用输出绑定(这是函数的“魔力”的很大一部分;))docs.microsoft.com/en-us/azure/azure-functions/… 使用例如 CloudBlobDirectory 作为输出绑定
  • 谢谢。我快速阅读了链接,但我不明白如何在我的情况下使用 CloudBlobDirectory
  • 我稍后会整理一个例子
  • 查看修改后的答案。我添加了一个完整的例子

标签: c# azure azure-functions


【解决方案1】:

除了我上面的评论(无论如何请这样做!),你的问题在这里:

blockBlob.UploadFromStreamAsync(fileStream);

它需要改为:

await blockBlob.UploadFromStreamAsync(fileStream);

还有你的函数本身:

public static async Task Run([BlobTrigger("input-files/{name}", Connection = "connectionStorage")] Stream myBlob, string name, TraceWriter log)

//编辑:这里有一个使用输出绑定的完整示例。请注意,如果您愿意,当然也可以使用两个不同的连接字符串(两个不同的存储帐户)进行触发器和输出绑定。

using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;

namespace SampleFunctions
{
    public static class UnzipBlob
    {
        /// <summary>
        /// This function is triggered by new blobs (should be a ZIP file) 
        /// and extracts the contents of the zip as new, individual blobs to a storage account
        /// </summary>
        /// <param name="inputBlob"></param>
        /// <param name="inputBlobName"></param>
        /// <param name="outputContainer"></param>
        /// <param name="log"></param>
        /// <returns></returns>
        [FunctionName("UnzipBlob")]
        public static async Task Run([BlobTrigger("input-zips/{inputBlobName}", Connection = "AzureWebJobsStorage")] Stream inputBlob, string inputBlobName,
            Binder binder,
            ILogger log)
        {
            log.LogInformation($"Blob trigger function received blob\n Name:{inputBlobName} \n Size: {inputBlob.Length} Bytes");

            if (Path.GetExtension(inputBlobName)?.ToLower() == ".zip")
            {
                // We use the first char of the input file name as a dynamic part in the container. (Note: You should check if this is a valid char for the container name)
                var container = $"my-dynamic-container-{inputBlobName.Substring(0,1).ToLower()}";
                var attributes = new Attribute[]
                {
                        new BlobAttribute($"{container}", FileAccess.ReadWrite),
                        new StorageAccountAttribute("AzureWebJobsStorage")
                };
                var outputContainer = await binder.BindAsync<CloudBlobContainer>(attributes);
                await outputContainer.CreateIfNotExistsAsync();

                var archive = new ZipArchive(inputBlob);
                foreach (var entry in archive.Entries)
                {
                    // we write the output files to a directory with the same name as the input blob. Change as required
                    var blockBlob = outputContainer.GetBlockBlobReference($"{inputBlobName}/{entry.FullName}");
                    using (var fileStream = entry.Open())
                    {
                        if (entry.Length > 0)
                        {
                            log.LogInformation($"Extracting - {entry.FullName} to - {blockBlob.Name}");
                            await blockBlob.UploadFromStreamAsync(fileStream);
                        }
                    }
                }
            }
            else
            {
                log.LogInformation("Not a zip file. Ignoring");
            }
        }
    }
}

【讨论】:

  • 谢谢。多么愚蠢的错误!我从视频中复制代码。无论如何,我不明白为什么在开发中,使用 Visual Studio,它可以工作。
  • 非常感谢您的代码。我注意到输出容器存储在函数的参数中。我必须根据文件文件输入的名称更改输出容器。我如何使用您的解决方案?
  • 您需要写入的所有容器是否已经存在,还是您还需要动态创建它们?
  • 没关系,得到了一个解决方案,在任何一种情况下都可以使用Binder 在运行时绑定。你可以用它做一些非常好的事情;)docs.microsoft.com/en-us/azure/azure-functions/…我更新了上面的代码
猜你喜欢
  • 1970-01-01
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 2012-01-19
  • 2020-04-11
  • 2015-03-24
  • 2013-12-21
  • 1970-01-01
相关资源
最近更新 更多