Question

Code below is used to save PostgreSql database backup from browser in Apache Mono MVC2 web application in Linux.

It takes long time for backup to finish before file transfer begins. pg_dump can write to standard output instead of file. How to force controller to pipe standard output to browser without creating temporary file ? Or how to show some progress indicator to user ?

[Authorize] 
public class BackupController : ControllerBase 
 { 
   [AcceptVerbs(HttpVerbs.Get)] 
   public ActionResult Backup() 
   { 
            var pinfo = new ProcessStartInfo(); 
            var fn = "temp.backup"; 
            pinfo.Arguments = " -f \"" + fn + "\" -Fc -h \"" + "myserver" + "\" -U \"" +                 "postgres" + " \"" + "mydb" + "\""; 
            pinfo.FileName = "/usr/lib/pgsql/pg_dump"; 
            pinfo.UseShellExecute = false; 
            using (var process = new Process()) 
            { 
                process.EnableRaisingEvents = true; 
                process.StartInfo = pinfo; 
                process.Start(); 
                while (!process.HasExited) 
                    Thread.Sleep(2000); 
                process.WaitForExit(); 
                if (process.ExitCode!=0) 
                  throw new Exception("error"); 
                process.Close(); 
            } 
            Response.ClearContent(); 
            Response.WriteFile(fn); 
            Response.End(); 
            System.IO.File.Delete(fn); 
            return null; 
        } 
} 

update

I tried code below according to answer. Backup copy saved from browser causes pg_restore to crash. How to use binary write or something other to create correct backup ? Also browser prompts for save only after pg_dump has finished. How to implement pipe so that data is transferred over internet if pd_dump is working ?

[Authorize]
public class BackupController : ControllerBase
{
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Backup()
    {
        Response.ClearContent();
        Response.AddHeader("content-disposition", string.Format("attachment; filename=\"backup.backup\"");
        Response.ContentType = "application/backup";
        using (var process = new Process())
        {
            process.StartInfo.Arguments = " -ib -Z6 -Fc -h \"server\"";
            process.StartInfo.FileName = "/usr/lib/pgsql/pg_dump";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            Server.ScriptTimeout = 86400;
            process.Start();
            while (!process.HasExited)
            {
                var b = process.StandardOutput.ReadToEnd();
                Response.Write(b);
                Thread.Sleep(2000);
            }
            process.WaitForExit();
            if (process.ExitCode != 0)
            {
                return new ContentResult()
                {
                    Content = "Error " + process.ExitCode.ToString()
                };
            }
            var b2 = process.StandardOutput.ReadToEnd();
            Response.Write(b2);
            process.Close();
            Response.End();
            return null;
        }
    }
Was it helpful?

Solution

You can redirect standard output to stream using RedirectStandardOutput and then read from Process.StandardOutput which enables to you to show some progress (combined with AJAX or so, which you probably know well).

OTHER TIPS

Maybe write the output stream to a temp buffer (file or memory stream) while another thread pipes to the browser with singnalR? Here's a good video demonstrating signalR: http://channel9.msdn.com/Shows/Web+Camps+TV/Damian-Edwards-and-David-Fowler-Demonstrate-SignalR

It would allow you to send data to the browser in real time as the stream contents change. Also, you say there was corruption, I only say this cause it's bit me more than once, make sure you serialized and deserialize your output with the same character encoding.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top