Azure API 调用在控制台应用程序中工作,而不是在 MVC 中工作

Azure API call working in Console App & not in MVC

我有一个 asp.net MVC 应用程序,允许用户将 PDF/image 上传到系统。然后我想将此 PDF 发送到 Azure Read API 并将返回的文本存储在系统上的 .text 文件中(这样我可以稍后将一些数据输入数据库)。

我在控制台应用程序中测试它时,它工作得很好,尽管当我尝试将它实现到我的 MVC 网络应用程序中时,它无法工作;当我上传 PDF 时,文件被上传,但没有其他任何事情发生,即没有使用返回的数据创建文本文件。当我在控制台应用程序中使用相同的 Azure API 方法尝试此操作时,它工作正常(文件是使用返回的文本创建的)

我的控制器:

    public ActionResult Upload()
            {
                return View();
            }

            [HttpPost]
            public ActionResult Upload(HttpPostedFileBase file)
            {
                string filename = Guid.NewGuid() + Path.GetExtension(file.FileName);
                string filepath = @"C:\Users385\source\repos\BookingSystem\BookingSystem\Surveys\" + filename;
                file.SaveAs(Path.Combine(Server.MapPath("/Surveys"), filename));
                AzureVisionAPI.ExtractToTextFile(filepath);


                return View();
            }

我的 Azure API 调用辅助方法:

namespace BookingSystem.Helpers
{
    static class AzureVisionAPI
    {
        static string subscriptionKey = ("SUBSCRIPTON_KEY");
        static string endpoint = ("END_POINT");

        public static ComputerVisionClient Authenticate(string endpoint, string key)
        {
            ComputerVisionClient client =
                new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
                { Endpoint = endpoint };
            return client;
        }


        public static void ExtractToTextFile(string filename)
        {
            ComputerVisionClient client = Authenticate(endpoint, subscriptionKey);
            ExtractTextLocal(client, filename).Wait();
        }

        public static async Task ExtractTextLocal(ComputerVisionClient client, string localImage)
        {

            // Helps calucalte starting index to retrieve operation ID
            const int numberOfCharsInOperationId = 36;

            using (Stream imageStream = File.OpenRead(localImage))
            {
                // Read the text from the local image
                BatchReadFileInStreamHeaders localFileTextHeaders = await client.BatchReadFileInStreamAsync(imageStream);
                // Get the operation location (operation ID)
                string operationLocation = localFileTextHeaders.OperationLocation;

                // Retrieve the URI where the recognized text will be stored from the Operation-Location header.
                string operationId = operationLocation.Substring(operationLocation.Length - numberOfCharsInOperationId);

                // Extract text, wait for it to complete.
                int i = 0;
                int maxRetries = 10;
                ReadOperationResult results;
                do
                {
                    results = await client.GetReadOperationResultAsync(operationId);

                    await Task.Delay(1000);
                    if (maxRetries == 9)
                    {
                        throw new Exception("Azure API timed out");
                    }
                }
                while ((results.Status == TextOperationStatusCodes.Running ||
                        results.Status == TextOperationStatusCodes.NotStarted) && i++ < maxRetries);

                // Display the found text.
                var textRecognitionLocalFileResults = results.RecognitionResults;
                foreach (TextRecognitionResult recResult in textRecognitionLocalFileResults)
                {
                    using (StreamWriter sw = new StreamWriter(@"C:\Users385\source\repos\BookingSystem\BookingSystem\surveytest.txt"))
                    {
                        foreach (Line line in recResult.Lines)
                        {
                            sw.WriteLine(line.Text);
                        }
                    }
                }
            }
        }
    }
}

您可能会在此处看到异步死锁。而不是使用 .Wait() 你应该等待 Task return 从你的异步方法中编辑。

发生的事情是你的第一个 await 方法 returns,然后调用 .Wait() 的方法,这会阻塞线程并独占访问你当前的 SynchronizationContext,当内部等待的事情完成时,下面的代码在同一个 SynchronizationContext 上排队,它被阻塞了。 .Wait() 永远不会停止阻塞,因为 Task 永远无法完成。这被称为异步死锁。

这里处理它的最佳方法是使您的控制器异步并 return Task<ActionResult>,然后使用 async/await 自上而下。另一种处理方法是将 .ConfigureAwait(false) 分散到所有 Taskawait,但是在处理异步工作时最好不要阻塞。

您的代码可能如下所示:

控制器:

public ActionResult Upload()
            {
                return View();
            }

            [HttpPost]
            public async Task<ActionResult> Upload(HttpPostedFileBase file)
            {
                string filename = Guid.NewGuid() + Path.GetExtension(file.FileName);
                string filepath = @"C:\Users385\source\repos\BookingSystem\BookingSystem\Surveys\" + filename;
                file.SaveAs(Path.Combine(Server.MapPath("/Surveys"), filename));
                await AzureVisionAPI.ExtractToTextFile(filepath);


                return View();
            }

AzureVisionAPI:

namespace BookingSystem.Helpers
{
    static class AzureVisionAPI
    {
        static string subscriptionKey = ("SUBSCRIPTON_KEY");
        static string endpoint = ("END_POINT");

        public static ComputerVisionClient Authenticate(string endpoint, string key)
        {
            ComputerVisionClient client =
                new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
                { Endpoint = endpoint };
            return client;
        }


        public static async Task ExtractToTextFile(string filename)
        {
            ComputerVisionClient client = Authenticate(endpoint, subscriptionKey);
            await ExtractTextLocal(client, filename);
        }

        public static async Task ExtractTextLocal(ComputerVisionClient client, string localImage)
        {

            // Helps calucalte starting index to retrieve operation ID
            const int numberOfCharsInOperationId = 36;

            using (Stream imageStream = File.OpenRead(localImage))
            {
                // Read the text from the local image
                BatchReadFileInStreamHeaders localFileTextHeaders = await client.BatchReadFileInStreamAsync(imageStream);
                // Get the operation location (operation ID)
                string operationLocation = localFileTextHeaders.OperationLocation;

                // Retrieve the URI where the recognized text will be stored from the Operation-Location header.
                string operationId = operationLocation.Substring(operationLocation.Length - numberOfCharsInOperationId);

                // Extract text, wait for it to complete.
                int i = 0;
                int maxRetries = 10;
                ReadOperationResult results;
                do
                {
                    results = await client.GetReadOperationResultAsync(operationId);

                    await Task.Delay(1000);
                    if (maxRetries == 9)
                    {
                        throw new Exception("Azure API timed out");
                    }
                }
                while ((results.Status == TextOperationStatusCodes.Running ||
                        results.Status == TextOperationStatusCodes.NotStarted) && i++ < maxRetries);

                // Display the found text.
                var textRecognitionLocalFileResults = results.RecognitionResults;
                foreach (TextRecognitionResult recResult in textRecognitionLocalFileResults)
                {
                    using (StreamWriter sw = new StreamWriter(@"C:\Users385\source\repos\BookingSystem\BookingSystem\surveytest.txt"))
                    {
                        foreach (Line line in recResult.Lines)
                        {
                            sw.WriteLine(line.Text);
                        }
                    }
                }
            }
        }
    }
}