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

Azure上ASP.NET Core用IronPDF 2021.3.1生成PDF遇文件占用冲突

Azure App Service中IronPDF并发生成PDF时的IOException问题

问题描述

在Azure App Service托管的ASP.NET Core应用中使用IronPDF生成PDF,多用户并发场景下偶尔会抛出如下IOException:

The process cannot access the file 'C:\home\site\wwwroot\IronTemp\Temp{GUID}.tmp' because it is being used by another process.

已在GeneratePdf<T>方法中实现带指数退避的重试逻辑,但错误仍偶发。怀疑问题源于IronPDF依赖全局静态属性IronPdf.Installation.TempFolderPath存储临时文件,并发请求互相干扰导致文件冲突。尝试为每个请求创建唯一临时文件夹,但IronPDF不会自动使用该路径;考虑过用全局锁强制串行生成,但会严重影响并发性能。

核心问题:能否配置IronPDF为每个请求使用独立临时文件夹,或在Azure App Service并发环境中避免文件访问冲突?

额外信息

  • 应用环境:Azure App Service上的ASP.NET Core
  • IronPDF版本:2021.3.1
  • 问题触发条件:偶发于高并发场景

附当前实现代码:

public async Task<byte[]> GeneratePdf<T>(ICollection<T> data, string template, string variablePrefix, PdfOptions pdfOptions, int maxRetries, int retryDelay)
{
    if (data == null)
        throw new NullReferenceException("Kolekcja danych do wypełnienia nie może pozostać pusta!");

    if (String.IsNullOrEmpty(variablePrefix))
        throw new NullReferenceException("Prefix do zmiennej w szablonie nie może pozostać pusty!");

    if (String.IsNullOrEmpty(template))
        throw new NullReferenceException("Nie podano szablonu do wypełnienia!");

    int delay = retryDelay;   
    
    for (int attempt = 0; attempt < maxRetries; attempt++)
    {
        try
        {
            string filledTemplate = await FillTemplate(template, data, variablePrefix);

            IronPdf.HtmlToPdf renderer = new IronPdf.HtmlToPdf();

            if (pdfOptions.PageLayout == PageLayout.Horizontal)
                renderer.PrintOptions.PaperOrientation = PdfPaperOrientation.Landscape;
            else
                renderer.PrintOptions.PaperOrientation = PdfPaperOrientation.Portrait;

            renderer.PrintOptions.FitToPaperWidth = true;
            renderer.PrintOptions.Zoom = 95;

            var pdf = renderer.RenderHtmlAsPdf(filledTemplate);

            return pdf.BinaryData;
        }
        catch (IOException ex) when (ex.Message.Contains("because it is being used by another process"))
        {
            if (attempt < maxRetries - 1)
            {
                Console.WriteLine($"Błąd IO, plik w użyciu. Próba {attempt + 1} z {maxRetries}. Opóźnienie {delay} ms.");
                
                await Task.Delay(delay);
            }
            else
            {
                throw new Exception("Nie udało się wygenerować pliku pdf po wielokrotnych próbach. Plik wciąż jest w użyciu.", ex);
            }
        }
        catch (Exception ex)
        {
            throw new Exception("Nie udało się wygenerować pliku pdf. Zgłoszono następujący błąd: " + ex.Message);
        }
    }

    return null;
}

解决方案

1. 为每个请求配置独立临时文件夹(推荐)

IronPDF 2021.3.1支持通过IronPdf.Installation.TempFolderPath动态设置临时路径,结合请求级别的唯一文件夹创建,可避免并发冲突。关键是在每个请求的渲染周期内设置独立路径,并在请求结束后清理:

修改后的代码示例:

public async Task<byte[]> GeneratePdf<T>(ICollection<T> data, string template, string variablePrefix, PdfOptions pdfOptions, int maxRetries, int retryDelay)
{
    if (data == null)
        throw new ArgumentNullException(nameof(data), "Kolekcja danych do wypełnienia nie może pozostać pusta!");
    if (String.IsNullOrEmpty(variablePrefix))
        throw new ArgumentNullException(nameof(variablePrefix), "Prefix do zmiennej w szablonie nie może pozostać pusty!");
    if (String.IsNullOrEmpty(template))
        throw new ArgumentNullException(nameof(template), "Nie podano szablonu do wypełnienia!");

    int delay = retryDelay;
    string tempFolder = null;
    
    for (int attempt = 0; attempt < maxRetries; attempt++)
    {
        try
        {
            // 创建当前请求专属的临时文件夹
            tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            Directory.CreateDirectory(tempFolder);
            // 绑定IronPDF使用该临时路径
            IronPdf.Installation.TempFolderPath = tempFolder;

            string filledTemplate = await FillTemplate(template, data, variablePrefix);

            IronPdf.HtmlToPdf renderer = new IronPdf.HtmlToPdf();
            if (pdfOptions.PageLayout == PageLayout.Horizontal)
                renderer.PrintOptions.PaperOrientation = PdfPaperOrientation.Landscape;
            else
                renderer.PrintOptions.PaperOrientation = PdfPaperOrientation.Portrait;

            renderer.PrintOptions.FitToPaperWidth = true;
            renderer.PrintOptions.Zoom = 95;

            var pdf = renderer.RenderHtmlAsPdf(filledTemplate);
            return pdf.BinaryData;
        }
        catch (IOException ex) when (ex.Message.Contains("because it is being used by another process"))
        {
            if (attempt < maxRetries - 1)
            {
                Console.WriteLine($"Błąd IO, plik w użyciu. Próba {attempt + 1} z {maxRetries}. Opóźnienie {delay} ms.");
                await Task.Delay(delay);
                delay *= 2; // 指数退避优化
            }
            else
            {
                throw new Exception("Nie udało się wygenerować pliku pdf po wielokrotnych próbach. Plik wciąż jest w użyciu.", ex);
            }
        }
        catch (Exception ex)
        {
            throw new Exception("Nie udało się wygenerować pliku pdf. Zgłoszono następujący błąd: " + ex.Message, ex);
        }
        finally
        {
            // 清理临时文件夹,避免磁盘占用
            if (tempFolder != null && Directory.Exists(tempFolder))
            {
                try
                {
                    Directory.Delete(tempFolder, recursive: true);
                }
                catch (Exception)
                {
                    // 可记录日志,不抛出异常避免影响主流程
                }
            }
        }
    }

    return null;
}

原理:虽然TempFolderPath是静态属性,但ASP.NET Core中每个请求在独立线程执行,设置路径后立即同步执行渲染,渲染完成后清理,不会和其他请求的配置互相干扰。

2. 升级IronPDF到最新版本

新版本IronPDF优化了临时文件命名规则,减少并发冲突概率,同时可能提供实例级别的临时路径配置(无需依赖全局静态属性),从根源降低冲突风险。

3. 代码优化建议

  • 异常类型替换:将NullReferenceException改为ArgumentNullException,符合.NET参数校验的规范。
  • 异常包装:捕获非IO异常时,保留原始异常栈信息(如示例中添加ex作为内部异常),便于排查问题。
  • 指数退避:重试时延迟翻倍,提升高并发场景下的重试成功率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 03:07:02