1

I am trying to create a web service that returns a pdf file as a byte[] and then the app that consumes it grabs the byte[] and saves it as a pdf file and then opens it. The file fails to open at the end.

Here is the Web Service that returns a byte[]

[WebMethod]
public byte[] XXXX(int fileID)
{
    try
    {
        using (EntitiesModel dbContext = new EntitiesModel())
        {   
            string fileFullPath = .....
            .......     
            if (fileFullNamePath != null)
            {
                FileStream fileStream = new FileStream(fileFullNamePath, FileMode.Open, System.IO.FileAccess.Read);
                int len = fileStream.Length.ToInt();
                Byte[] documentContents = new byte[len];
                fileStream.Read(documentContents, 0, len);
                fileStream.Close();
                return documentContents;

Then it is called from an app with the following code

string soap = "<?xml version=\"1.0\" encoding=\"utf - 8\"?>" +
              "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
              "<soap:Body>" +
              "<XXXX xmlns=\"http://tempuri.org/\">" +
              "<fileID>XXXXX</fileID>" +
              "</XXXX>" +
              "</soap:Body>" +
              "</soap:Envelope>";
string localhostContext = @"http://localhost:3381/";
string webserviceAddress = @"XXXX/XXXX/XXXXX.asmx";
string url = localhostContext + webserviceAddress ;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "text/xml";
request.ContentLength = soap.Length;
request.Timeout = 20000;
request.Method = "POST";
using (Stream stream = request.GetRequestStream())
{
    using (StreamWriter streamWriter = new StreamWriter(stream))
    {
        streamWriter.Write(soap);               }
    }
}
byte[] bytes;
try
{
    WebResponse response = request.GetResponse();
    bytes = ReadFully(response.GetResponseStream());
}
catch (Exception exception)
{
    throw;
}

private byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    using (MemoryStream memoryStream = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            memoryStream.Position = 0;
            memoryStream.Write(buffer, 0, read);
        }
        return memoryStream.ToArray();
    }
}

FileStream objfilestream =
                new FileStream(fileName, FileMode.Create,FileAccess.ReadWrite);
objfilestream.Write(bytes, 0, bytes.Length);
objfilestream.Close();
var process = Process.Start(fileName);

The code runs fine and creates a pdf and then tries to open that pdf. But the file can not be opened. Adobe Acrobat gives the error

Adobe Acrobat Reader could not open XXX.pdf because it is either not a 
supported file type or because the file has been damaged (for example, it 
was sent as an email attachment and wasn't correctly decoded).

Because I am not getting an error in the code I am at a loss to know where the error is that is not creating the proper file.

There was an issue with the Stream variable called input was not giving length so I used Jon Skeet's suggestion here Stackoverflow:Creating a byte array from a stream

new byte[16*1024]; 

rather than

new byte[input.length]
Darren
  • 1,352
  • 5
  • 19
  • 49
  • 2
    Have you tried opening the resulting PDF in a text editor like Notepad++? The response is probably base64-encoded or otherwise wrapped. – CodeCaster May 21 '19 at 06:16
  • @CodeCaser It opens in NotePad++ as 1 line. – Darren May 21 '19 at 06:21
  • 1
    @DarrenWood I think he's hinting that your data might be corrupt. At least shows us what you see on your screen. Kindly paste some example NotePad++ text here or else give a link to your not-working PDF file (share file via: Dropbox or Google Drive, etc). – VC.One May 21 '19 at 06:34
  • @CodeCaster shouldn't that be _"open resulting PDF in a **hex** editor"_? Easier that way to check if bytes are a valid (PDF) format. – VC.One May 21 '19 at 06:35
  • @VC.One I will post the file tommorw. I am away from desk now. Thanks – Darren May 21 '19 at 07:08
  • 2
    @DarrenWood No worries. Also you likely don't want `memoryStream.Position = 0;` to be inside the While loop since it's resetting position each time to zero. You're overwriting each previous `memoryStream.Write`. I think that's why you end up with just 1 line. – VC.One May 21 '19 at 07:22
  • @VC.One That very much looks like the issue (at least like one major of it). You should make that an answer. – mkl May 21 '19 at 08:49
  • @VC.One will I implement tomorrow morning – Darren May 21 '19 at 09:00
  • @VC.One I have removed the code memoryStream.Position = 0 from the while loop but I am still getting the error. I noticed that the soap xml tags with the byte[] in a XXXResult tag are saved to the file. I am not sure how to extract the byte[] path. I am researching this. The soap XML tags and the binary file are still on 1 line which is strange. – Darren May 22 '19 at 01:04
  • @CodeCaster Yes it was wrapped in the SOAP Xml response . How do I extract the Byte[] from that? – Darren May 22 '19 at 01:05
  • @CodeCaster The solution you commented was correct. The response was both wrapped in Soap XML and base64 encoded. If you want to provide that as an answer I will accept it. – Darren May 22 '19 at 02:42
  • @VC.One Your comment was part of the solution so if you provide that as answer I will upvote it. – Darren May 22 '19 at 02:45
  • @DarrenWood maybe share **your** solution to finally get it working. Could be useful to others. Thanks – VC.One May 27 '19 at 20:19

1 Answers1

1

There were three things wrong.

memoryStream.Position = 0;

in the while loop was problematic so I removed it.

Secondly when reading the stream. What it returned was the SOAP XMl message with the encoded base64 string in the the XXXXResult XML tag. So I had to extract that.

Finally I had to use

byte[] fileResultBytes  = Convert.FromBase64String(resultString);

to get the byte[] from the resultString extracted from the SOAP message. In the test SOAP message, that can be generated locally, it tells you the type of this result string. I missed that initially.

Thanks to VC.One and CodeCaster for their correct suggestions.

Darren
  • 1,352
  • 5
  • 19
  • 49