web-dev-qa-db-ja.com

Web APIアプリケーションからPDFを返す方法

サーバーで実行されているWeb APIプロジェクトがあります。 2つの異なる種類のソースからPDFを返すことになっています。実際のポータブルドキュメントファイル(PDF)と、データベースに保存されたbase64文字列です。私が抱えている問題は、ドキュメントをクライアントMVCアプリケーションに送り返すことです。これの残りは、起こったことすべてと私がすでに試したことの詳細です。

これらの2つの形式をC#コードに変換し、(=)PDF形式に変換します。これらのドキュメントの1つを表すはずのバイト配列を正常に転送しましたが、ブラウザに(それ自体で新しいタブに)表示することができず、常に何らかの「表示できない」エラーが発生します。

最近、サーバー側でドキュメントを表示する方法を作成し、少なくともそれを実行できるようにしました。ドキュメントをコードに取得し、FileStreamResultを作成してそれを(暗黙的なキャスト)ActionResultとして返します。サーバー側のMVCコントローラーに戻り、ブラウザーでPDFが正常に表示される)単純な戻り(ビューなし)に戻すことを取得しました。しかし、単純にWeb API関数は、FileStreamResultのJSON表現のように見えるものを単に返します。

クライアントMVCアプリケーションに適切に返すようにしようとすると、「_ buffer」を直接設定できないことがわかります。返されてオブジェクトにスローされるプロパティの一部がプライベートであり、アクセスできないというエラーメッセージ。

前述のPDFのバイト配列表現は、base64文字列に変換されると、FileStreamResultによってJSONで返される「_buffer」文字列と同じ文字数を持たないようです。最後に約26k個の 'A'がありません。

これを取得する方法についてのアイデアPDFを正しく返すには?必要に応じてコードを提供できますが、PDFサーバー側のWeb APIアプリケーションをクライアント側のMVCアプリケーションに接続し、ブラウザでWebページとして表示します。

追伸「クライアント側」のアプリケーションが技術的にはクライアント側ではないことを知っています。サーバーアプリケーションにもなりますが、この場合は問題ではありません。 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のインスタンスを作成できませんでした。タイプはインターフェースまたは抽象クラスであり、インスタンス化できません。パス「FileStream」。

空白の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
21
Doctor93

返すサーバー側のコード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));
}

お役に立てれば

26
Nkosi