2

I'm calling ExportToPDF2 method in a for loop to produce pdf documents.

The problem is the loop stops after the Respons.End().

How can I get this resolved, and is there a better way than using this technique?

private int ExportToPDF2()
{
    using (StringWriter sw = new StringWriter())
    {
        using (HtmlTextWriter hw = new HtmlTextWriter(sw))
        {
            Page.RenderControl(hw);
            StringReader sr = new StringReader(sw.ToString());
            Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 10f, 0f);
            PdfWriter writer = PdfWriter.GetInstance(pdfDoc, Response.OutputStream);
            pdfDoc.Open();
            XMLWorkerHelper.GetInstance().ParseXHtml(writer, pdfDoc, sr);
            pdfDoc.Close();
            Response.ContentType = "application/pdf";
            Response.AddHeader("content-disposition", "attachment;filename=GridViewExport.pdf");
            Response.Cache.SetCacheability(HttpCacheability.NoCache);
            Response.Write(pdfDoc);
            Response.End();
        }                
    }
    return 0;
}
Daniel B
  • 3,109
  • 2
  • 33
  • 42
click2000
  • 23
  • 1
  • 6
  • You can't download multiple documents at once like that. You could generate a bunch of PDFs on the server, then put them in some sort of archive file format (such as a zip file) and then allow the user to download that one file. – mason Oct 20 '17 at 13:39
  • Thanks for the response. Can you kindly shed some light on the code I need to write, i.e. a sample. All the samples I have seen so far involve a web form where Response.End() is used. When I try to write code in a c# class I can't use Response, or do I need to references to my code in the "using" section? your help is gratefully appreciated. – click2000 Oct 20 '17 at 14:03
  • What he is saying is don't write to the response. Write the files to disk, then present the user with a page that lists all the files so he can download by clicking on each link. – JuanR Oct 20 '17 at 14:05
  • I'm saying don't call `Response.End`. A user can't download multiple files in a single request. They can only download one. So you need to change your process: generate multiple files, then provide a link to download each file separately. Or zip all the files together and provide them in a single download. Your choice. No, I'm not going to write sample code for you. I described how to do it, now it's up to you to implement it. – mason Oct 20 '17 at 14:10
  • I get what you are saying. At this point I am not thinking about the user, just to output the pdf document from the loop. I just want a sample code on how to write to disk instead of to Response. – click2000 Oct 20 '17 at 14:15
  • Check out the `System.IO namespace`: https://msdn.microsoft.com/en-us/library/system.io(v=vs.110).aspx – JuanR Oct 20 '17 at 14:18
  • 2
    Then research how! When a question comes to mind, your first inclination should not be to ask Stack Overflow how to do it. Do a web search, see how other people do it. Try to implement it. If you get stuck, provide an [MCVE](https://stackoverflow.com/help/mcve). – mason Oct 20 '17 at 14:19
  • Well why do you think I asked the question. I can't find what I am looking for out there, all I have found is Response based. I see that Juan has also provided a link. I will check that out, thank you. – click2000 Oct 20 '17 at 14:25
  • You asked the question because you didn't know how to handle downloading multiple files. I described a strategy to do that, and before you even had time to attempt implementing my strategy or do sufficient research to start, you asked me to provide an implementation. Take some personal responsibility: research before asking for help. It's fine to not know everything, it's not fine to ask questions without making an effort at solving the problem yourself. – mason Oct 20 '17 at 15:02
  • I know the strategy, I am hoping that someone had the answer. Obviously you don't. Thanks for your help anyway. – click2000 Oct 20 '17 at 15:07
  • I've been developing software professionally for 7 years now. I can of course generate some PDF's and provide them to a user for downloading. "The answer" is that you need to learn how to do research and attempt to solve your problem yourself before asking for help. You will learn much better that way, and you won't waste other people's time. You're new to web development and Stack Overflow. That's great! Let's get started on the right foot though. Asking for someone to just write your code for you isn't getting started on the right foot. – mason Oct 20 '17 at 15:10
  • Please, if you don't have the answer don't comment any further, thank you. – click2000 Oct 20 '17 at 15:14
  • 1
    Possible duplicate of [Download multiple files with a single action](https://stackoverflow.com/questions/2339440/download-multiple-files-with-a-single-action) – mason Oct 20 '17 at 15:27

1 Answers1

0

It strange to me that you are saving the output of same page (Page.RenderControl(hw);) multiple times!

Anyway, It's not possible to send multiple outputStream from Server, you can try

Response.Write(somefile);
Response.End();
textBox1.Text = "1111"; // it wont work because you can't have multiple outputstream

The best workaround you can have is to make a zip file then do Response.Write(zipFile)

But if you still insist on having them separately(multiple downloads) better you save each file on the server first by:

using (HtmlTextWriter hw = new HtmlTextWriter(sw))
{
  Page.RenderControl(hw);
  StringReader sr = new StringReader(sw.ToString());
  Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 10f, 0f);
  PdfWriter.GetInstance(pdfDoc, new FileStream(Server.MapPath("~") + pdfName + ".pdf");
} 

then use this trick https://stackoverflow.com/a/30682695/336511 to support multiple download. note that this will work on modern browsers.

var links = [
  'https://s3.amazonaws.com/Minecraft.Download/launcher/Minecraft.exe',
  'https://s3.amazonaws.com/Minecraft.Download/launcher/Minecraft.dmg',
  'https://s3.amazonaws.com/Minecraft.Download/launcher/Minecraft.jar'
];

function downloadAll(urls) {
  var link = document.createElement('a');

  link.setAttribute('download', null);
  link.style.display = 'none';

  document.body.appendChild(link);

  for (var i = 0; i < urls.length; i++) {
    link.setAttribute('href', urls[i]);
    link.click();
  }

  document.body.removeChild(link);
}
<button onclick="downloadAll(window.links)">Test me!</button>
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Daniel B
  • 3,109
  • 2
  • 33
  • 42
  • Thanks Daniel. I really appreciate you reply. I will give it a shot. – click2000 Oct 20 '17 at 20:47
  • Actually it still fails. I guess the form is not allowing the loop to iterate as you point out above. The zip file is not going to work either in my case. There are no users to look at these documents except the customers who are off site. The document is to be e-mailed immediately after its produced. I have dug out another method using a stored procedure. Not pretty, but it works for now. Thanks Daniel for your contribution. – click2000 Oct 21 '17 at 01:08
  • @click2000 yrwelcome. appreciated if u can accept my answer or at least upvote my answer. up to u :) – Daniel B Oct 21 '17 at 01:19
  • I just did :). Have a nice weekend! – click2000 Oct 21 '17 at 01:41
  • 1
    Just to update you, I managed to iterate successfully using iTextSharp and adding to Daniel's feedback after visiting this link http://www.c-sharpcorner.com/blogs/create-table-in-pdf-using-c-sharp-and-itextsharp. Thanks all for your help. – click2000 Oct 21 '17 at 21:52
  • Hey Daniel, I selected your feedback to be the correct answer. – click2000 Oct 22 '17 at 15:24
  • @click2000 tnx man. cliché: pleasure doing business with u :))) – Daniel B Oct 22 '17 at 15:26
  • Daniel, I just want to mention an interesting point. At the top you are saying what I am doing is strange to you. This is called "bursting" in reporting software where you can produce the same report over and over using a loop of unique parameter values and save the output to disk. I had hoped that I could do the same in web forms. – click2000 Oct 22 '17 at 21:30
  • @click2000 I meant save Page.RenderControl(hw) into a private field and then use it. But if u r making changes each time after calling ExportToPDF2, im not sure if it's possible(u were getting exception) but try Request.Flush (?) – Daniel B Oct 23 '17 at 01:15
  • The form produces the output nicely on the first iteration but when it loops again I get this error: "A page can have only one server-side Form tag". – click2000 Oct 23 '17 at 01:44
  • Here is the latest:using (StringWriter sw = new StringWriter()){ using (HtmlTextWriter hw = new HtmlTextWriter(sw)) { Page.RenderControl(hw); StringReader sr = new StringReader(sw.ToString()); Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 10f, 0f); PdfWriter writer = PdfWriter.GetInstance(pdfDoc, new FileStream("C:\\PdfBurst\\test" + counter + ".pdf", FileMode.Create, FileAccess.Write, FileShare.None)); pdfDoc.Open(); XMLWorkerHelper.GetInstance().ParseXHtml(writer, pdfDoc, sr); pdfDoc.Close(); return 0; } } – click2000 Oct 23 '17 at 01:45
  • However, starting the loop from a cs class and iterating using iTextSharp, I got the loop to work flawlessly, but I loose the benefit of having a well formatted good looking report. – click2000 Oct 23 '17 at 01:48
  • this is what I thought too at first when u said iTextSharp, poor UI. I'll put some time tmr if I can to find a bettter workaround. What r u ding in the for loop other than ExportToPDF2, btw? – Daniel B Oct 23 '17 at 02:01
  • 1
    I bind data to the controls (gridview, labels etc..) sitting on the from then I call ExportToPDF2(); – click2000 Oct 23 '17 at 02:05
  • try this: move the controls to a new user control and create appropriate public methods to bind data. then use this approach https://stackoverflow.com/a/31619749/336511 instead of Page page = new Page(); u can do var page = new MyAmazingBasePage(); – Daniel B Oct 23 '17 at 14:45
  • Thanks Daniel, I will look into. I appreciate your help and feedback. I will let you know if I get it working. – click2000 Oct 23 '17 at 16:54