You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在for循环中使用await调用MS Graph转PDF接口的异步优化问题

问题描述

我通过MS Graph调用转PDF接口,将生成的base64编码字符串返回给前端供pdf.js处理。为提升处理速度,我想用async/await实现尽可能多的接口并行调用(不使用MS Graph的批量功能),但不确定当前代码是串行等待每个await结果还是能循环发起请求。我需要的是循环发起所有请求,待全部完成后再返回最终结果。

当前代码

public async Task<ActionResult> PDFVersionsToBase64([FromForm] string ID)
{
    //Using FormData on frontend
    //checking ID exists on searching in dashboard
    if (String.IsNullOrEmpty(ID))
    {
        return Ok(new { Result = "Failed" });
    }
    else
    {

        String query = "select * from dbo.function(@ID)";
        using (var connection = new SqlConnection(connectionString))
        {
            var documents = connection.Query<QuestionDocuments>(query, new { ID = ID});

            foreach (var file in documents)
            {
                var fullPath = Path.Combine(fileDirectory, file.path);
                using (var memoryStream = await oneDrive.DriveItemConvertToPdfAsync(SharedDriveID, fullPath, "path"))
                {
                    byte[] buffer = new byte[memoryStream.Length];
                    memoryStream.Read(buffer, 0, (int)memoryStream.Length);
                     file.Base64 = Convert.ToBase64String(buffer);
                }
            }

            return Ok(documents);

        }
    }
}

public async Task<Stream> DriveItemConvertToPdfAsync(string DriveID, string PathOrDriveItemID, string SearchType)
{
    Stream response = null;
    try
    {
        var queryOptions = new List<QueryOption>()
        {
            new QueryOption("format", "pdf")
        };

        if (SearchType == "path")
        {
            response= await _graphServiceClient.Me.Drives[DriveID].Root
            .ItemWithPath(PathOrDriveItemID)
            .Content
            .Request(queryOptions)
            .GetAsync();
        }
        else if (SearchType == "id")
        {
            response = await _graphServiceClient.Me.Drives[DriveID].Items[PathOrDriveItemID]
            .Content
            .Request(queryOptions)
            .GetAsync();
        }
    }
    catch (ServiceException ex)
    {
        Console.WriteLine($"Error deleting file: {ex.ToString()}");
    }
    return response;
}

当前代码的问题

你现在的foreach循环中每次都直接await异步方法,这会导致串行执行:必须等当前文件的转PDF请求完全完成,才会发起下一个文件的请求,完全没有利用异步并行的优势。

并行改造方案

要实现并行,核心是先批量创建所有异步任务(发起请求),再统一等待所有任务完成。具体改造如下:

改造后的代码

public async Task<ActionResult> PDFVersionsToBase64([FromForm] string ID)
{
    // 使用前端FormData
    // 检查ID有效性
    if (String.IsNullOrEmpty(ID))
    {
        return Ok(new { Result = "Failed" });
    }

    string query = "select * from dbo.function(@ID)";
    using (var connection = new SqlConnection(connectionString))
    {
        // 转为List方便后续操作
        var documents = connection.Query<QuestionDocuments>(query, new { ID = ID}).ToList();

        // 批量创建异步任务,此时所有请求已经开始发起
        var tasks = documents.Select(async file =>
        {
            var fullPath = Path.Combine(fileDirectory, file.path);
            using (var memoryStream = await oneDrive.DriveItemConvertToPdfAsync(SharedDriveID, fullPath, "path"))
            {
                if (memoryStream != null)
                {
                    byte[] buffer = new byte[memoryStream.Length];
                    // 改用异步读取流,避免阻塞线程
                    await memoryStream.ReadAsync(buffer, 0, (int)memoryStream.Length);
                    file.Base64 = Convert.ToBase64String(buffer);
                }
                else
                {
                    // 标记转换失败,可根据需求自定义
                    file.Base64 = null;
                }
            }
        }).ToList();

        // 等待所有异步任务全部完成
        await Task.WhenAll(tasks);

        return Ok(documents);
    }
}

// 保留原方法,优化异常提示
public async Task<Stream> DriveItemConvertToPdfAsync(string DriveID, string PathOrDriveItemID, string SearchType)
{
    try
    {
        var queryOptions = new List<QueryOption>()
        {
            new QueryOption("format", "pdf")
        };

        if (SearchType == "path")
        {
            return await _graphServiceClient.Me.Drives[DriveID].Root
                .ItemWithPath(PathOrDriveItemID)
                .Content
                .Request(queryOptions)
                .GetAsync();
        }
        else if (SearchType == "id")
        {
            return await _graphServiceClient.Me.Drives[DriveID].Items[PathOrDriveItemID]
                .Content
                .Request(queryOptions)
                .GetAsync();
        }
    }
    catch (ServiceException ex)
    {
        Console.WriteLine($"转换PDF出错: {ex.ToString()}");
    }
    return null;
}

关键说明

  • 并行核心:Select(async file => ...)会为每个文档生成一个Task对象,此时所有转PDF请求已经同时发起,而非等待上一个完成才开始
  • 统一等待:Task.WhenAll(tasks)会阻塞当前线程直到所有异步任务完成,确保所有文件都处理完毕后再返回结果
  • 异步流读取:将memoryStream.Read替换为await memoryStream.ReadAsync,避免线程阻塞,进一步提升异步效率
  • 容错处理:单个文件转换失败不会导致整个请求失败,可通过file.Base64的状态标记转换结果,后续可根据需求调整失败逻辑

内容的提问来源于stack exchange,提问作者Qiuzman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:20:14