在 .Net ApiController 中启动文件下载

Initiate file download in .Net ApiController

我正在尝试生成一个包含 HTML 内容的 PDF 文件,该文件是通过 AJAX 从 JavaScript 发送的。

我已正确发送 HTML,并使用 HTML 正确生成 PDF,但我一直在尝试弄清楚如何在用户浏览器上启动文件下载.

这就是我目前正在尝试的。这在直接点击文件的 URL 时在 IHttpHandler 文件中有效,但在通过 AJAX.

发布时在 ApiController 中无效

我也尝试过将结果发布到 IHttpHandler 文件。一切正常,直到我尝试使用 BinaryWrite.

启动下载
public class DownloadScheduleController : ApiController
{
    public void Post(HtmlModel data)
    {
        var htmlContent = data.html;

        var pdfBytes = (new NReco.PdfGenerator.HtmlToPdfConverter()).GeneratePdf(htmlContent);

        using (var mstream = new System.IO.MemoryStream())
        {
            HttpContext.Current.Response.ContentType = "application/pdf";
            HttpContext.Current.Response.AppendHeader("content-disposition", "attachment; filename=UserSchedule.pdf");
            HttpContext.Current.Response.BinaryWrite(pdfBytes);
            HttpContext.Current.Response.End();
        }
    }
}

这是 Ajax 请求。它使用 Angular 的 $http.

$http({
    method: 'POST',
    url: 'api/downloadSchedule',
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    data: data
});

或者我可以 return 将 PDF 二进制文件 JavaScript,但是我如何使用 JavaScript 保存 pdf?

任何帮助将不胜感激。

出于安全原因,浏览器无法从 AJAX 请求启动文件下载。只能通过导航到发送文件的页面来完成。

通常,如果您需要从 Javascript 开始下载,您可以设置 window.location.href = urlToFile,或者创建一个指向 url.

的新 iframe

在你的情况下这不会起作用,因为上面的方法只执行一个 GET 请求,而你需要一个 POST,所以我只看到两个可能的解决方案:

  • 您可以将您的 JS 修改为 - 而不是使用 $http 提交请求 - 创建一个 HTML 表单,其中包含与您最初 post 使用 AJAX 编辑的字段相对应的字段请求,然后将表单附加到页面,填充字段并提交表单
  • 或者您可以更改服务器端代码以及 Javascript。

如果您选择第二种解决方案,您可以遵循在服务器端使用两种方法的方法:

  • 一个 POST 生成文件并将其保存在缓存中(可能有更好的解决方案使用缓存,特别是如果您在服务器场中,但让我们继续很简单)

  • 一个 GET 检索缓存的内容并将其 returns 提供给用户。通过这种方式,您将能够通过 AJAX 调用 post 数据,然后通过向右导航 url.

  • 来获取文件

您的 C# 代码将如下所示:

public class DownloadScheduleController : ApiController
{
    public object Post(HtmlModel data)
    {
        var htmlContent = data.html;

        var pdfBytes = (new NReco.PdfGenerator.HtmlToPdfConverter()).GeneratePdf(htmlContent);
        var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(30), Priority = CacheItemPriority.NotRemovable };
        var cacheId = Guid.NewGuid().ToString();
        MemoryCache.Default.Add("pdfBytes_" + cacheId, pdfBytes, policy);
        return new { id = cacheId };
    }

    public void Get(string id)
    {
        var pdfBytes = MemoryCache.Default.Get("pdfBytes_" + id);
        MemoryCache.Default.Remove("pdfBytes_" + id);

        using (var mstream = new System.IO.MemoryStream())
        {
            HttpContext.Current.Response.ContentType = "application/pdf";
            HttpContext.Current.Response.AppendHeader("content-disposition", "attachment; filename=UserSchedule.pdf");
            HttpContext.Current.Response.BinaryWrite(pdfBytes);
            HttpContext.Current.Response.End();
        }
    }
}

您的前端可能类似于:

$http({
    method: 'POST',
    url: 'api/downloadSchedule',
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    data: data
}).success(function(response) {
    // note: the url path might be different depending on your route configuration
    window.location.href = 'api/downloadSchedule/' + response.id;
});

请记住,我的代码只是为了向您展示方法,并不意味着在生产中使用它。例如,您绝对应该在使用后立即清除缓存。在使用之前,您还应该在 Get 方法中对从缓存中检索到的内容进行完整性检查。此外,您可能希望根据您的要求对存储和检索的数据采取额外的安全预防措施。