簡體   English   中英

如何從 Web API 應用程序返回 PDF

[英]How to return a PDF from a Web API application

我有一個在服務器上運行的 Web API 項目。 它應該從兩種不同的來源返回 PDF:實際的可移植文檔文件 (PDF) 和存儲在數據庫中的 base64 字符串。 我遇到的問題是將文檔發送回客戶端 MVC 應用程序。 剩下的就是關於所發生的一切以及我已經嘗試過的細節。

我編寫的代碼成功地將這兩種格式轉換為 C# 代碼,然后(返回)為 PDF 格式。 我已經成功傳輸了一個應該代表這些文檔之一的字節數組,但我無法讓它在瀏覽器中顯示(在一個新選項卡中)。 我總是收到某種“無法顯示”的錯誤。

最近,我做了一個在服務器端查看文檔的方法,以確保我至少可以讓它這樣做。 它將文檔放入代碼中並用它創建一個 FileStreamResult,然后它作為(隱式轉換)ActionResult 返回。 我讓它返回到服務器端 MVC 控制器並將其放入一個簡單的返回(無視圖)中,在瀏覽器中顯示 PDF 就好了。 但是,嘗試直接使用 Web API 函數只會返回看起來像 FileStreamResult 的 JSON 表示的內容。

當我嘗試讓它正確返回到我的客戶端 MVC 應用程序時,它告訴我不能直接設置“_buffer”。 一些錯誤消息,表明某些返回並拋出到對象中的屬性是私有的,無法訪問。

PDF 的上述字節數組表示,當轉換為 base64 字符串時,似乎與 FileStreamResult 在 JSON 中返回的“_buffer”字符串的字符數不同。 最后缺少大約 26k 'A。

關於如何讓這個 PDF 正確返回的任何想法? 如有必要,我可以提供代碼,但必須有一些已知的方法將 PDF 從服務器端 Web API 應用程序返回到客戶端 MVC 應用程序,並將其顯示為瀏覽器中的網頁。

PS 我確實知道“客戶端”應用程序在技術上不在客戶端。 它也將是一個服務器應用程序,但在這種情況下應該無關緊要。 相對於 Web API 服務器,我的 MVC 應用程序是“客戶端”。

獲取pdf的代碼

private System.Web.Mvc.ActionResult GetPDF()
{
    int bufferSize = 100;
    int startIndex = 0;
    long retval;
    byte[] buffer = new byte[bufferSize];
    MemoryStream stream = new MemoryStream();
    SqlCommand command;
    SqlConnection sqlca;
    SqlDataReader reader;

    using (sqlca = new SqlConnection(CONNECTION_STRING))
    {
        command = new SqlCommand((LINQ_TO_GET_FILE).ToString(), sqlca);
        sqlca.Open();
        reader = command.ExecuteReader(CommandBehavior.SequentialAccess);
        try
        {
            while (reader.Read())
            {
                do
                {
                    retval = reader.GetBytes(0, startIndex, buffer, 0, bufferSize);
                    stream.Write(buffer, 0, bufferSize);
                    startIndex += bufferSize;
                } while (retval == bufferSize);
            }
        }
        finally
        {
            reader.Close();
            sqlca.Close();
        }
    }
    stream.Position = 0;
    System.Web.Mvc.FileStreamResult fsr = new System.Web.Mvc.FileStreamResult(stream, "application/pdf");
    return fsr;
}

從 GetPDF 獲取的 API 函數:

    [AcceptVerbs("GET","POST")]
    public System.Web.Mvc.ActionResult getPdf()
    {
        System.Web.Mvc.ActionResult retVal = GetPDF();
        return retVal;
    }

用於顯示 PDF 服務器端:

public ActionResult getChart()
{
    return new PDFController().GetPDF();
}

MVC 應用程序中的代碼隨着時間的推移發生了很大變化。 現在的方式,它沒有達到它試圖在瀏覽器中顯示的階段。 在此之前它會出錯。

public async Task<ActionResult> get_pdf(args,keys)
{
    JObject jObj;
    StringBuilder argumentsSB = new StringBuilder();
    if (args.Length != 0)
    {
        argumentsSB.Append("?");
        argumentsSB.Append(keys[0]);
        argumentsSB.Append("=");
        argumentsSB.Append(args[0]);
        for (int i = 1; i < args.Length; i += 1)
        {
            argumentsSB.Append("&");
            argumentsSB.Append(keys[i]);
            argumentsSB.Append("=");
            argumentsSB.Append(args[i]);
        }
    }
    else
    {
        argumentsSB.Append("");
    }
    var arguments = argumentsSB.ToString();
    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        var response = await client.GetAsync(URL_OF_SERVER+"api/pdf/getPdf/" + arguments).ConfigureAwait(false);
        jObj = (JObject)JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
    }
    return jObj.ToObject<ActionResult>();
}

我直接從 Web API 控制器運行該方法得到的 JSON 是:

{
    "FileStream":{
        "_buffer":"JVBER...NjdENEUxAA...AA==",
        "_origin":0,
        "_position":0,
        "_length":45600,
        "_capacity":65536,
        "_expandable":true,
        "_writable":true,
        "_exposable":true,
        "_isOpen":true,
        "__identity":null},
    "ContentType":"application/pdf",
    "FileDownloadName":""
}

我縮短了“_buffer”,因為它太長了。 我目前在 get_pdf(args,keys) 的返回行上收到以下錯誤消息

異常詳細信息:Newtonsoft.Json.JsonSerializationException:無法創建 System.Web.Mvc.ActionResult 類型的實例。 類型是接口或抽象類,不能被實例化。 路徑“文件流”。

回到我曾經得到一個空白的pdf閱讀器(閱讀器是空白的。沒有文件)時,我使用了以下代碼:

public async Task<ActionResult> get_pdf(args,keys)
{
    byte[] retArr;
    StringBuilder argumentsSB = new StringBuilder();
    if (args.Length != 0)
    {
        argumentsSB.Append("?");
        argumentsSB.Append(keys[0]);
        argumentsSB.Append("=");
        argumentsSB.Append(args[0]);
        for (int i = 1; i < args.Length; i += 1)
        {
            argumentsSB.Append("&");
            argumentsSB.Append(keys[i]);
            argumentsSB.Append("=");
            argumentsSB.Append(args[i]);
        }
    }
    else
    {
        argumentsSB.Append("");
    }
    var arguments = argumentsSB.ToString();
    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/pdf"));
        var response = await client.GetAsync(URL_OF_SERVER+"api/webservice/" + method + "/" + arguments).ConfigureAwait(false);
        retArr = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
    }
    var x = retArr.Skip(1).Take(y.Length-2).ToArray();
    /*Response.Clear();
    Response.ClearContent();
    Response.ClearHeaders();
    Response.ContentType = "application/pdf";
    Response.AppendHeader("Content-Disposition", "inline;filename=document.pdf");
    Response.BufferOutput = true;
    Response.BinaryWrite(x);
    Response.Flush();
    Response.End();*/
    return new FileStreamResult(new MemoryStream(x),MediaTypeNames.Application.Pdf);
    }

注釋掉的是來自其他一些嘗試的代碼。 當我使用該代碼時,我從服務器返回了一個字節數組。 它看起來像:

JVBER...NjdENEUx

一些服務器端代碼返回PDF(Web Api)。

[HttpGet]
[Route("documents/{docid}")]
public HttpResponseMessage Display(string docid) {
    HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest);
    var documents = reader.GetDocument(docid);
    if (documents != null && documents.Length == 1) {
        var document = documents[0];
        docid = document.docid;
        byte[] buffer = new byte[0];
        //generate pdf document
        MemoryStream memoryStream = new MemoryStream();
        MyPDFGenerator.New().PrintToStream(document, memoryStream);
        //get buffer
        buffer = memoryStream.ToArray();
        //content length for use in header
        var contentLength = buffer.Length;
        //200
        //successful
        var statuscode = HttpStatusCode.OK;
        response = Request.CreateResponse(statuscode);
        response.Content = new StreamContent(new MemoryStream(buffer));
        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
        response.Content.Headers.ContentLength = contentLength;
        ContentDispositionHeaderValue contentDisposition = null;
        if (ContentDispositionHeaderValue.TryParse("inline; filename=" + document.Name + ".pdf", out contentDisposition)) {
            response.Content.Headers.ContentDisposition = contentDisposition;
        }
    } else {
        var statuscode = HttpStatusCode.NotFound;
        var message = String.Format("Unable to find resource. Resource \"{0}\" may not exist.", docid);
        var responseData = responseDataFactory.CreateWithOnlyMetadata(statuscode, message);
        response = Request.CreateResponse((HttpStatusCode)responseData.meta.code, responseData);
    }
    return response;
}

在我的視圖上你可以做這樣的事情

<a href="api/documents/1234" target = "_blank" class = "btn btn-success" >View document</a>

它將調用web api並在瀏覽器的新選項卡中打開PDF文檔。

這是我基本上做同樣的事情,但從MVC控制器

// NOTE: Original return type: FileContentResult, Changed to ActionResult to allow for error results
[Route("{docid}/Label")]
public ActionResult Label(Guid docid) {
    var timestamp = DateTime.Now;
    var shipment = objectFactory.Create<Document>();
    if (docid!= Guid.Empty) {
        var documents = reader.GetDocuments(docid);
        if (documents.Length > 0)
            document = documents[0];

            MemoryStream memoryStream = new MemoryStream();
            var printer = MyPDFGenerator.New();
            printer.PrintToStream(document, memoryStream);

            Response.AppendHeader("Content-Disposition", "inline; filename=" + timestamp.ToString("yyyyMMddHHmmss") + ".pdf");
            return File(memoryStream.ToArray(), "application/pdf");
        } else {
            return this.RedirectToAction(c => c.Details(id));
        }
    }
    return this.RedirectToAction(c => c.Index(null, null));
}

希望這可以幫助

我需要從 .NET core 3.1 web api 返回一個 pdf 文件,並找到了這篇優秀的文章:

https://codeburst.io/download-files-using-web-api-ae1d1025f0a9

基本上,你打電話:

var bytes = await System.IO.File.ReadAllBytesAsync(pathFileName);
return File(bytes, "application/pdf", Path.GetFileName(pathFileName));

整個代碼是:

using Microsoft.AspNetCore.Mvc;
using System.IO;

using Reportman.Drawing;
using Reportman.Reporting;
using System.Text;
using System.Threading.Tasks;

[Route("api/[controller]")]
[ApiController]
public class PdfController : ControllerBase
{
    [HttpGet]
    [Route("ot/{idOT}")]
    public async Task<ActionResult> OT(string idOT)
    {
        Report rp = new Report();
        rp.LoadFromFile("ot-net.rep"); // File created with Reportman designer
        rp.ConvertToDotNet();

        // FixReport
        rp.AsyncExecution = false;
        PrintOutPDF printpdf = new PrintOutPDF();

        // Perform the conversion from one encoding to the other.
        byte[] unicodeBytes = Encoding.Convert(Encoding.ASCII, Encoding.Unicode, Encoding.ASCII.GetBytes($"Orden de trabajo {idOT}"));
        string unicodeString = new string(Encoding.Unicode.GetChars(unicodeBytes));
        // todo: convert to unicode
        // e = Encoding.GetEncoding(unicodeString);
        // System.Diagnostics.Trace.WriteLine(e);
        if (rp.Params.Count > 0)
        {
            rp.Params[0].Value = unicodeString;
        }

        printpdf.FileName = $"ot-{idOT}.pdf";
        printpdf.Compressed = false;

        if (printpdf.Print(rp.MetaFile))
        {
            // Download Files using Web API. Changhui Xu. https://codeburst.io/download-files-using-web-api-ae1d1025f0a9
            var bytes = await System.IO.File.ReadAllBytesAsync(printpdf.FileName);
            return File(bytes, "application/pdf", Path.GetFileName(printpdf.FileName));
        }

        return null;
    }

調用這個 API 看起來像:https://localhost:44387/api/pdf/ot/7

Reportman 是一個 pdf 生成器,您可以在以下位置找到: https ://reportman.sourceforge.io/

享受!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM