如何从Web API方法返回URL Access生成的PDF

时间:2016-02-04 15:39:10

标签: c# pdf asp.net-web-api reporting-services urlaccess

我已经看过许多使用Web API从存储在磁盘上的文件返回PDF(或其他文件类型)的示例。但是,在我的情况下,我尝试使用SSRS的URL访问动态生成文档。以下是URL的示例:

https://reporting.mydomain.biz/_layouts/ReportServer/RSViewerPage.aspx?rv:RelativeReportUrl=%2fquest%2fQUEST%2520Reports%2fReview.rdl&rp%3aReview=220

我尝试过多种方法,但其中大多数都是在2012年,并且与ASP.Net MVC相关,例如:

public async Task<FileStreamResult> GenerateReport()
{

    CredentialCache credentialCache = new CredentialCache();
    credentialCache.Add(new Uri("http://domainORipaddress"), "NTLM", new NetworkCredential(
        ConfigurationManager.AppSettings["username"],
        ConfigurationManager.AppSettings["password"]
    ));

    Stream report = null;
    using (var httpClient = new HttpClient(new HttpClientHandler { Credentials = credentialCache }))
    {

        httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
        report = await httpClient.GetStreamAsync("reportUrl");
    }

    var contentDisposition = new ContentDisposition
    {
        FileName = "Report.pdf",
        Inline = false
    };
    Response.AppendHeader("Content-Disposition", contentDisposition.ToString());
    return File(report, "application/pdf");    

    //or use
    //Response.AppendHeader("Content-Disposition", String.Format("attachment;filename=\"{0}\"", reportName));
    //return File(reportPath, MediaTypeNames.Application.Pdf);
}

此示例代码来自此网站:http://webstackoflove.com/sql-server-reporting-service-with-asp-net-mvc/

这是一个类似的问题,但基于ASP.NETReporting services: Get the PDF of a generated report

我尝试使用上面的代码返回HttpResponseMessage,但我找回了没有数据的文件。

以下是我现在使用的内容,并且它返回的文件为0 KB:

public HttpResponseMessage PrintQualityReview([FromUri] int reviewId)
{
    var reportUrl = String.Format("https://reporting.mydomain.biz/quest/_vti_bin/reportserver?https://reporting.mydomain.biz/quest/QUEST%20Reports/{0}Review.rdl&rs:Format=PDF&Review={1}",
    ConfigurationManager.AppSettings["ReportingServiceDatabase"],
    reviewId);

    CredentialCache credentialCache = new CredentialCache();
    credentialCache.Add(new Uri("https://reporting.mydomain.biz"), "NTLM", new NetworkCredential(
        ConfigurationManager.AppSettings["ReportingServiceAccount"],
        ConfigurationManager.AppSettings["ReportingServiceAccountPwd"]
    ));

    MemoryStream mStream = new MemoryStream();

    using (WebClient wc = new WebClient()) 
    {
        wc.Credentials = credentialCache;

        using (Stream stream = wc.OpenRead(reportUrl))
        {
            stream.CopyTo(mStream);
        }
    }            

    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = new StreamContent(mStream);
    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
    response.Content.Headers.ContentDisposition.FileName = "Report.pdf";

    return response;
}

我想知道如何使用ASP.Net Web API完成他的工作?

2 个答案:

答案 0 :(得分:0)

获得0字节的原因是因为您没有设置响应头的ContentLength

这是您更新的代码

public HttpResponseMessage PrintQualityReview([FromUri] int reviewId) {
        var reportUrl = String.Format("https://reporting.mydomain.biz/quest/_vti_bin/reportserver?https://reporting.mydomain.biz/quest/QUEST%20Reports/{0}Review.rdl&rs:Format=PDF&Review={1}",
            ConfigurationManager.AppSettings["ReportingServiceDatabase"],
            reviewId);

        CredentialCache credentialCache = new CredentialCache();
        credentialCache.Add(new Uri("https://reporting.mydomain.biz"), "NTLM", new NetworkCredential(
            ConfigurationManager.AppSettings["ReportingServiceAccount"],
            ConfigurationManager.AppSettings["ReportingServiceAccountPwd"]
        ));

        MemoryStream mStream = new MemoryStream();

        using (WebClient wc = new WebClient()) {
            wc.Credentials = credentialCache;

            using (Stream stream = wc.OpenRead(reportUrl)) {
                stream.CopyTo(mStream);
            }
        }

        var contentLength = mStream.Length; //content length for header
        var contentType = new MediaTypeHeaderValue("application/pdf");
        var contentDispositionValue = "attachment; filename=Report.pdf";

        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StreamContent(mStream);
        response.Content.Headers.ContentType = contentType;
        response.Content.Headers.ContentLength = contentLength;
        ContentDispositionHeaderValue contentDisposition = null;            
        if (ContentDispositionHeaderValue.TryParse(contentDispositionValue , out contentDisposition)) {
            response.Content.Headers.ContentDisposition = contentDisposition;
        }

        return response;
    }

答案 1 :(得分:0)

Unfortunately, simply adding content-length to the header didn't do the trick. The trick was to have the AngularJS $http service return an ArrayBuffer response.

Web API

update data1
  set full_text = (regexp_matches(full_text, 'I [0-9]{1,3}'))[1]
where full_text ~ 'I [0-9]{1,3}'

AngularJS

In the controller:

        [System.Web.Http.HttpGet]
        [System.Web.Http.Route("api/print/qualityreview/{reviewId:int}")]
        public HttpResponseMessage PrintQualityReview([FromUri] int reviewId)
        {
            logger.Info("Printing quality review for QR Id {0}", reviewId);

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
            var reportUrl = String.Format("https://example.com/quest/_vti_bin/reportserver?https://example.com/quest/QUEST%20Reports/{0}Review.rdl&rs:Format=PDF&Review={1}",
                ConfigurationManager.AppSettings["ReportingServiceDatabase"],
                reviewId);            
            CredentialCache credentialCache = new CredentialCache();
            credentialCache.Add(new Uri("https://example.com"), "NTLM", new NetworkCredential(
                ConfigurationManager.AppSettings["ReportingServiceAccount"],
                ConfigurationManager.AppSettings["ReportingServiceAccountPwd"]
            ));


            using (WebClient webClient = new WebClient())
            {
                webClient.Credentials = credentialCache;
                var reportStream = webClient.OpenRead(reportUrl);

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    reportStream.CopyTo(memoryStream);

                    response.Content = new ByteArrayContent(memoryStream.ToArray());  //new StreamContent(reportStream);
                    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
                    response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
                    //response.Content.Headers.ContentLength = reportStream.Length;
                    response.Content.Headers.ContentDisposition.FileName = "QualityReview.pdf";
                    response.StatusCode = HttpStatusCode.OK;

                    return response;
                }
            }
        }
    }

The service call:

/**
* Prints the quality review
*/
function printQualityReview() {
    questAPIService.printQualityReview(reviewId)
        .then(function (response) {
            var outputFilename = vm.reviewee.lastName + ', ' + vm.reviewee.firstName + ' ' + $filter('date')(vm.review.reviewDate, 'MMddyyyy') + '.pdf';
            var blob = new Blob([response.data], { type: 'application/pdf' });                    
            saveAs(blob, outputFilename);

        }, function (error) {
            $log.error('Printing failed: ', error);
            toastr.error('There was an error attempting to print the quality review.');
        });
}

The trick was to make sure to return a byte array in the response content and set the response type as ArrayBuffer when calling the API method.