Search code examples
c#asp.net-mvcpostgresqlmonomod-mono

how to pipe stdout to browser in Mono Apache MVC2 application


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

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).