J
john
The standard method to transmit a file from an aspx page to a browser
is to stream the file to the response then end the response. The HTML
code generated by the aspx page is discarded, and the browser displays
or offers to save the binary file instead.
I would like to have the browser accept and display the revised HTML
code as well as offering to open or save the attached file. This
SHOULD be possible using a multipart MIME format - one part is declared
with a Content-Disposition of inline and a Content-Type of text/html,
and a second part with a Content-Disposition of attachment and a
Content-Type of application/octet-stream.
However, I haven't been able to get a valid multipart response out of
my aspx page (using multipart/mixed or multipart/x-mixed-replace). The
Page wrapper seems to be hard-coded to creating and sending headers
that are incompatible with this approach.
Has anyone succesfully done something like this? My example code is
below, which is a simple page that displays a file and updates a view
count. When the page is refreshed, the view count increments. When a
file is displayed, the view count is incremented in code but the
revised HTML is not sent to the browser, so you don't see the textbox
change. Also, since the ViewState is not updated in the browser, the
view count goes back to it's previous state when you next refresh.
I've tried removing the Response.End() at the end of the ShowDocument
method, but bad things happen (the next button click generates a
bizarre page that has the html code displayed twice with a partial HTTP
header between them...). I also tried manually setting the
Response.ContentType to multipart/mixed and writing the boundary
between the HTML output and the file streaming, but I couldn't change
the ContentType after the inital headers had been sent.
Thanks for any ideas...
John H.
== WebForm1.aspx ==
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs"
AutoEventWireup="false" Inherits="ShowDocumentTest.WebForm1" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
<head>
<title>WebForm1</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name=vs_defaultClientScript content="JavaScript">
<meta name=vs_targetSchema
content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body >
<form id="Form1" method="post" runat="server">
<p><asp:TextBox id="TextBox1" runat="server"
Width="400px"></asp:TextBox></p>
<p><asp:LinkButton id="btnPDF" runat="server">Show
file</asp:LinkButton></p>
<p><asp:Button id="Button1" runat="server"
Text="Refresh"></asp:Button></p>
</form>
</body>
</html>
== WebForm1.aspx.cs ==
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace ShowDocumentTest
{
public class WebForm1 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.LinkButton btnPDF;
protected System.Web.UI.WebControls.TextBox TextBox1;
protected System.Web.UI.WebControls.Button Button1;
private int viewCount = 0;
private void Page_Load(object sender, System.EventArgs e)
{
if (IsPostBack)
viewCount = (int)(ViewState["viewCount"]);
viewCount++;
ViewState["viewCount"] = viewCount;
TextBox1.Text = "You've seen this " + viewCount + " times";
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnPDF.Click += new System.EventHandler(this.btnPDF_Click);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
private void btnPDF_Click(object sender, System.EventArgs e)
{
ShowDocument(Request.MapPath("files") + Path.DirectorySeparatorChar
+ "example.pdf");
}
private void ShowDocument(string filePath)
{
FileInfo fi = new FileInfo(filePath);
string fileName = Path.GetFileName(filePath);
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=\""
+ fileName + "\"");
Response.AddHeader("Content-Length", fi.Length.ToString());
byte[] buffer = new byte[1024];
long byteCount;
FileStream inStr = File.OpenRead(filePath);
while ((byteCount = inStr.Read(buffer, 0, buffer.Length)) > 0)
{
if(Response.IsClientConnected)
{
Response.OutputStream.Write(buffer, 0, (int)(byteCount));
Response.Flush();
}
else
break;
}
inStr.Close();
Response.End();
}
}
}
is to stream the file to the response then end the response. The HTML
code generated by the aspx page is discarded, and the browser displays
or offers to save the binary file instead.
I would like to have the browser accept and display the revised HTML
code as well as offering to open or save the attached file. This
SHOULD be possible using a multipart MIME format - one part is declared
with a Content-Disposition of inline and a Content-Type of text/html,
and a second part with a Content-Disposition of attachment and a
Content-Type of application/octet-stream.
However, I haven't been able to get a valid multipart response out of
my aspx page (using multipart/mixed or multipart/x-mixed-replace). The
Page wrapper seems to be hard-coded to creating and sending headers
that are incompatible with this approach.
Has anyone succesfully done something like this? My example code is
below, which is a simple page that displays a file and updates a view
count. When the page is refreshed, the view count increments. When a
file is displayed, the view count is incremented in code but the
revised HTML is not sent to the browser, so you don't see the textbox
change. Also, since the ViewState is not updated in the browser, the
view count goes back to it's previous state when you next refresh.
I've tried removing the Response.End() at the end of the ShowDocument
method, but bad things happen (the next button click generates a
bizarre page that has the html code displayed twice with a partial HTTP
header between them...). I also tried manually setting the
Response.ContentType to multipart/mixed and writing the boundary
between the HTML output and the file streaming, but I couldn't change
the ContentType after the inital headers had been sent.
Thanks for any ideas...
John H.
== WebForm1.aspx ==
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs"
AutoEventWireup="false" Inherits="ShowDocumentTest.WebForm1" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
<head>
<title>WebForm1</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name=vs_defaultClientScript content="JavaScript">
<meta name=vs_targetSchema
content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body >
<form id="Form1" method="post" runat="server">
<p><asp:TextBox id="TextBox1" runat="server"
Width="400px"></asp:TextBox></p>
<p><asp:LinkButton id="btnPDF" runat="server">Show
file</asp:LinkButton></p>
<p><asp:Button id="Button1" runat="server"
Text="Refresh"></asp:Button></p>
</form>
</body>
</html>
== WebForm1.aspx.cs ==
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace ShowDocumentTest
{
public class WebForm1 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.LinkButton btnPDF;
protected System.Web.UI.WebControls.TextBox TextBox1;
protected System.Web.UI.WebControls.Button Button1;
private int viewCount = 0;
private void Page_Load(object sender, System.EventArgs e)
{
if (IsPostBack)
viewCount = (int)(ViewState["viewCount"]);
viewCount++;
ViewState["viewCount"] = viewCount;
TextBox1.Text = "You've seen this " + viewCount + " times";
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnPDF.Click += new System.EventHandler(this.btnPDF_Click);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
private void btnPDF_Click(object sender, System.EventArgs e)
{
ShowDocument(Request.MapPath("files") + Path.DirectorySeparatorChar
+ "example.pdf");
}
private void ShowDocument(string filePath)
{
FileInfo fi = new FileInfo(filePath);
string fileName = Path.GetFileName(filePath);
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=\""
+ fileName + "\"");
Response.AddHeader("Content-Length", fi.Length.ToString());
byte[] buffer = new byte[1024];
long byteCount;
FileStream inStr = File.OpenRead(filePath);
while ((byteCount = inStr.Read(buffer, 0, buffer.Length)) > 0)
{
if(Response.IsClientConnected)
{
Response.OutputStream.Write(buffer, 0, (int)(byteCount));
Response.Flush();
}
else
break;
}
inStr.Close();
Response.End();
}
}
}