我有一个在服务器上运行的Web API项目.它应该从两种不同的来源返回PDF:实际的可移植文档文件(PDF)和存储在数据库中的base64字符串.我遇到的麻烦是将文档发送回客户机MVC应用程序.剩下的就是我已经try 过的所有事情的细节.

我编写的代码成功地将这两种格式转换为C#代码,然后(返回)转换为PDF格式.我已经成功地传输了一个字节数组,该数组本应代表其中一个文档,但我无法将其显示在浏览器中(在新的选项卡中).我总是会遇到某种"无法显示"的错误.

最近,我设法在服务器端查看文档,以确保至少可以让它这样做.它将文档放入代码中并创建一个FileStreamResult,然后作为(隐式转换)ActionResult返回.我将其返回到服务器端MVC控制器,并将其放入一个简单的返回(无视图)中,在浏览器中显示PDF文件.然而,如果直接使用Web API函数,只会返回类似于FileStreamResult的JSON表示形式.

当我试图让它正确地返回到我的客户机MVC应用程序时,它告诉我不能直接设置"_buffer".一些错误消息,大意是返回并抛出到对象中的某些属性是私有的,无法访问.

前面提到的PDF字节数组表示形式在转换为Base64字符串时,似乎与FileStreamResult在JSON中返回的"_buffer"字符串的字符数不同.它最后少了大约26K‘A.

关于如何让这个PDF正确返回有什么 idea 吗?如果需要,我可以提供代码,但必须有某种已知的方法将PDF从服务器端Web API应用程序返回到客户端MVC应用程序,并在浏览器中将其显示为网页.

另外,我知道"客户端"应用程序在技术上并不在客户端.它也将是一个服务器应用程序,但在本例中这并不重要.相对于WebAPI服务器,我的MVC应用程序是"客户端".

Code

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",因为它太长了.

例外详细信息:Newtonsoft.Json.JsonSerializationException:无法创建System类型的实例.网状物Mvc.行动结果.类型是接口或抽象类,无法实例化.路径"文件流".

当我使用一个空白的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);
    }

注释掉的是来自其他一些try 的代码.当我使用该代码时,我从服务器返回一个字节array.它看起来像是:

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));
}

希望这有帮助

Asp.net相关问答推荐

502 DotNet WebApplication的网关nginx已损坏

使用 aspnet core SignalR 客户端为 VSIX 项目.

将命令行参数传递给 ASP.NET Core 中的 Startup 类

ASP.NET MVC4 jquery/javascript 包的使用

如何从 RouteData 获取路由名称?

使用 Lucene.NET 索引 .PDF、.XLS、.DOC、.PPT

如何在 ASP.NET 应用程序中使用 jQuery 捕获提交事件?

从 JavaScript 读取 web.config

通过 jQuery 调用 ASP.NET 服务器端方法

如何针对异常需求自定义 ASP.NET Web API AuthorizeAttribute

修改 web.config 时如何防止 ASP.NET 应用程序重新启动?

如何判断用户代理是 ipad 还是 iphone?

ASP.Net 错误:应用程序池的标识无效

解析器错误:此处不允许使用_Default,因为它没有扩展类System.Web.UI.Page和 MasterType 声明

在后面的代码中删除 css 类

ASP.NET MVC NonAction 含义

ASP.NET 中的嵌套中继器

在 ASP.NET Web API 2 中禁用 *all* 异常处理(为我自己腾出空间)?

重新生成designer.cs

Facebook 连接和 ASP.NET